テストとリカバリに関する議論:オブジェクト向けvs.関数式プログラミング

3750 ワード

Michael Feathersの最近のブログはブログコミュニティで異常に激しい論戦を引き起こした.Feathersは,いくつかのオブジェクト向けプログラミング言語の埋め込み特性がテストの進行に役立ち,オブジェクト向けプログラミング言語で作成されたコードを使用すると回復しやすいと述べた.
彼はこのような例を挙げて、class XにはbadMethodという方法があります.この方法は、製品データベースを呼び出して更新したり、最下位のハードウェアに関係するトランザクションを処理したりするなど、いくつかの「苦痛」な仕事を処理します.
public class X {
public void method() {
...
badMethod();
...
}
...
}

理想的な設計は、システムが一般的なクラスとクラスグループを独立してテストすることを可能にすることである.しかし、この例がこのような設計を実現していない場合、「badMethodは非finalであり、上書き可能な方法である」という事実は、「テストの十分な機動性」を得るために必要な柔軟性を提供するのに役立ちます.これは、「上書き機能とテストをより簡単にするための楔を作成することができる」ためです.
public class TestableX extends X { void badMethod() {//do nothing } }
Feathersはseam(継ぎ目)と呼ばれ、「修正する必要がなく、別の機能に代わる機能を使用できる場所」と呼ばれています.彼はOO言語が提供する緩やかなバインド技術が関数言語の回復よりも友好的であると信じている.
Feathers本人を含む一部の評論家は、ほとんどの言語がseamを提供できる事実を強調しています:プリプロセッサ、継承/多態性と委任、マクロと関数ポインタ、高次関数、動的関数、一等関数、モジュール境界またはmonads....一部の人は、本当にテスト性に関係しているのは、プログラミング言語ではなく下位設計の選択だと考えています.例えばジョン氏は、どんな言語を使っても「コードの構造はまずユニットテストの簡素化を考慮する必要がある」と断言した.もう一人のブログAndrewは、「コードの構造が必要なテストにそろっていなければ」と強調し、実現はテストに順応する方向に修正せざるを得ないと強調した.そこで、「『seam』に関する考え方は確かにテスト性を実現するために適切に設計された下位問題を狙っている」とコメントし、すなわちseamを適切に配置した.
これらの議論に対して、Feathersは、ほとんどの言語がseamを持っているにもかかわらず、コードがテストを容易に設計できなかった場合、どの言語がより使いやすいかが鍵だと強調しています.
私は「テストに対して設計する」ことが本当のポイントであることに同意しますが、私たちが何をしても、このような方法で設計されていないシステムがあることも知っています.そのため、回復性を非常に気にしています.
[…]
私はseamsの設計が可能であることを知っていますが、それは問題ではありません.本当の問題は、それらが設計に組み込まれていない場合に、それらがどれだけ簡単なのかを適切に配置することです.
[…] 
もちろん、seamsも常にあなたが実現したいテストの粒度と一致しているわけではありません.seamがすでに存在しているため、新しいseamを作成するのが簡単であるため、seamがよくサポートされている言語では、テストの粒度と一致するのはずっと簡単です.
Feathersによれば,同じ目的を達成するために関数言語で他のモデルを用いることができるが,「これは重い」がHaskellは例外である.Haskellでは、「テストで避けたいコードの大部分はmonadで実現できます」としています.
Feathersは「純粋な関数式は任意のユニットテストのニーズを満たすことができる」と議論することを知っているが、関数言語の詳細や関数言語が提供できる機会を考慮していないという評論家が多い.ErikdはFeathersがJavaコンストラクタと慣用的な方法を関数コードに応用していると感じた.
まず、Ocaml文法を使ってJavaコードを書いているように見え、OcamlがJavaに似ていないと文句を言った.彼の結論は少しも驚くべきものではない.OcamlはJavaのようなオブジェクト向けのコードを書くために設計されたものではなく、簡単です.
次に、Javaより関数言語の使用が難しいと主張した.Ocaml文法でJavaコードを書くのは確かに難しいかもしれませんが、一般的なOcamlコードや関数式コードを書くのはそれほど難しくありません.
多くの関数言語の擁護者は、関数プログラミングでは副作用がなく、Greg M.によると、この点は再構築が必要なコードを書くことを予防し、テストをより簡単にすることができると強調しています.
関数言語は、コード構造を最上位レベルですべての嫌なトランザクションを分離し、コードの純粋な論理を維持することができます.
[…] 
あなたのユニットが完全に独立した保障を持っている場合、ユニットテストはこのように簡単になります.あるいは、最悪でも明確で明らかな依存性を保証することができる.
Robert Goldman氏はまた、「通常のオブジェクト向けプログラミング言語で過度に採用されている状態は、テストにとって不利である」と議論した.なぜなら、人々は「テストにプラットフォームを提供するために、相互に関連する大きなオブジェクトエンティティを作成する必要がある.また、予想される副作用を検証するプロセスは、追加の複雑さをもたらす可能性がある」からだ.逆に「Haskellのような純粋な関数フレームワークでは、これらの問題はすべてMonadにカプセル化されている」.Greg Monadsが提案したように、「IOコマンドフローを作成するコードと、このIOフローを利用するコードの別のコードを記述し、これらのコマンドをどのように実行するかを決定する」ことができます.
Gregと同じ陣営から来たEricdは,関数式プログラミングには内部状態がないので,状態変化の処理もないと主張している.「状態遷移のないモデルやシステム」をテストするには、Feathersが述べたようなテストは必要ありません.
残りの必要な唯一のテストは、すべての境界条件をテストするために入力のセットを収集し、これらの入力を測定対象関数に渡し、出力を検証することです.
[...]
コンポーネントがテスト(すなわち純粋な関数)を分離し、テスト結果が関数が正しいことを示す場合、これらの純粋な関数のグループは当然正しい.
Feathersはこれに対して「純粋な関数式をよく理解しており、良い設計のコードを持っているのは自然にこれらの問題があるべきではないことも知っている」と答えた.彼は、すべてのコードが良い設計を持っているわけではないと強調し、「Haskellは確かに副作用を隔離させる関数式プログラミング言語の一つだが、OCamlやScalaのような他の言語は、「コードをめちゃくちゃにするのを避けることができないように見える」と強調した.いずれにしても,Feathersの見解に同意しない多くの論争者は,関数式コードをめちゃくちゃにする唯一の方法は,関数式言語を使用する際に非関数式用法を採用することであると考えている.Goldman氏は、副作用のあるプログラムは「ML、Ocaml、Common Lispのようなハイブリッド言語の非関数的な部分として公認されている」と断言し、明らかに避けなければならない.Gregは同様にこの観点を支持し、人々が関数言語と対立しなければならない限り、非関数的な使い方でコードを記述しない限り、「権威のあるOOコードから『得られる』IoCと注目点を分離することはできない」と述べた.これもErikdが、OO技術の背景がある人が関数言語で高品質のコードを書くためには、「古い習慣や考え方」を捨て、できるだけ長い間「オブジェクト向けや独断的なプログラミング特性」を忘れなければならないと主張している理由だ.
Debate about Testing and Recoverability:Object Oriented vs.Functional Programming Languages