web-dev-qa-db-ja.com

オブジェクト指向プログラミングでは、関数型プログラミングと比較して単体テストが難しいのはなぜですか?

これは series です。作者は、オブジェクト指向プログラミングでは状態が維持されるため、単体テストを書くのは難しいと述べています。彼はまた、関数型プログラミングは(常にではない)状態を維持しないので、単体テストを書く方が簡単だとも言っています。この問題を示す例は見当たりませんでした。これに該当する場合、オブジェクト指向プログラミングと関数型プログラミングの単体テストを比較する例を教えていただけますか?

9
user186961

オブジェクト指向プログラミングにアプローチする2つの異なる方法を区別したい

  1. Simulationist:オブジェクトは実際のドメインオブジェクトを表し、そのドメインに関連する機能を処理するようにプログラムしました。この方法でプログラムされたオブジェクトは、この機能を実装するために使用される多くの変更可能な状態と非表示の共同作業者を持つ可能性があります。
  2. レコード+関数:オブジェクトは、データとそのデータを操作する関数の単なるバンドルです。この方法でプログラムされたオブジェクトは、不変になりやすく、より少ない責任を引き受け、コラボレーターを注入することができます。

経験則として、最初の方法でプログラムされたオブジェクトは、2番目の方法よりも多くのメソッドとvoidメソッドを持つことになります。フライトシミュレータを作成して、飛行機のクラスを設計するとします。私たちは次のようなものになるでしょう:

_class Plane {
    void accelerate();
    void deccelerate();
    void toggleRightFlaps();
    void toggleLeftFlaps();
    void turnRudderRight();
    void turnRudderLeft();
    void deployLandingGear();
    void liftLandingGear();
    // etc.
    void tick() throws PlaneCrashedException;
}
_

これは、おそらく出会うよりも少し極端かもしれませんが、それは全体的に重要です。この種のインターフェースを実装したい場合は、オブジェクト内に保持する必要があります。

  1. 飛行機の機器の状態に関するすべての情報。
  2. 飛行機の速度/加速度に関するすべての情報。
  3. シミュレーションのリフレッシュレート(ティックを実装するため)。
  4. シミュレーションの3Dモデルとティックを実装するための物理に関する詳細。

モードで記述されたオブジェクトの単体テストを記述することは、次の理由により非常に困難です。

  1. テストの開始時に、このオブジェクトに必要なさまざまなデータとコラボレーターのすべてを提供する必要があります(これらを初期化するのは非常に面倒な場合があります)。
  2. メソッドをテストする場合、次の2つの問題が発生します。a)インターフェースがテストするのに十分なデータを公開していない場合が多い(したがって、モック/リフレクションを使用して期待を検証する必要がある)b)多くのコンポーネントがバインドされている各テストで動作を確認する必要があるものに。

基本的には、ある程度合理的でドメインによく似ているようなインターフェースから始めますが、シミュレーションの素晴らしさで、テストが非常に難しいオブジェクトを作成するようになっています。

ただし、同じ目的を満たすオブジェクトを作成できます。 Planeをより小さなビットに分割したいと思うでしょう。飛行機の物理的なビット、位置、速度、加速度、ロール、ヨーなどを追跡するPlaneParticleを用意し、これらを公開して操作できるようにします。次に、PlanePartsオブジェクトでステータスを追跡できます。 PlanePhysicsPlaneParticleを指定すると、新しいPlanePartsを吐き出す方法を知っている、たとえば、重力によってPlaneParticleオブジェクトがパラメータ化された、tick()を完全に異なる場所に出荷します。これはいくつかの例である必要はありませんが、これらはすべて完全に不変である可能性があります。

これで、テストに関して次の利点があります。

  1. 個々のコンポーネントは実行する必要が少なく、設定が簡単です。
  2. コンポーネントを個別にテストできます。
  3. これらのオブジェクトは内部を公開することで回避できるため(特にそれらが不変にされている場合)、そのため、オブジェクトを測定するための賢さは必要ありません。

これが秘訣です。私が説明した2番目のオブジェクト指向アプローチは、関数型プログラミングに非常に近いものです。おそらく、純粋な関数型プログラムでは、レコードと関数は別々であり、オブジェクトに結合されていません。関数型プログラムは、これらすべてのことを確実にします。単体テストが本当に簡単になると私が思うのは

  1. 小さなユニット(小さな状態空間)。
  2. 入力が最小限の関数(非表示の入力なし)。
  3. 最小限の出力で機能します。

関数型プログラミングはこれらのことを奨励します(ただし、どのパラダイムでも悪いプログラムを書くことができます)が、オブジェクト指向プログラムでは実現可能です。また、関数型プログラミングとオブジェクト指向プログラミングには互換性がないわけではないことも強調しておきます。

17
walpen