web-dev-qa-db-ja.com

基本クラスに共通ロジックを実装する

バックグラウンド

私が取り組んでいるプロジェクトのドキュメントで、次の文に出くわしました。すぐに私に警告が発せられました。

同じ基本クラスから継承する複数の具象クラスがある場合、すべてのサブクラスに共通のロジック[〜#〜] not [〜#〜]を基本クラスに実装する必要があります。

これは、私がOOPについて考えるすべてと矛盾する、かなり大胆な発言です。

述べられた理由はテスト容易性でした。ユニットテストを書くときに、共通のロジックが基本クラスに実装されている場合、それは各具象サブクラスに対して複数回テストされ、モックアウトできないと述べました。

質問

テストされているときに簡単に模倣できるように、複数のサブクラスに共通のロジックをどこにどのように実装できますか?

6
Omribitan

ユニットテストやTDDのディスカッションで、ラグの下に流されがちな大きな問題の1つに遭遇しました。オブジェクト指向の観点から適切に設計されたコードは、一般にユニットテストが困難です。通常、ユニットテストを記述しやすいコードは、オブジェクト指向設計のパラダイムを損なうことがあります。

単体テストへのほとんどのアプローチは、単体テストを容易にする設計に向かわせる傾向があり、これは、どのルールを曲げ/破壊するかによって悪くはありません。これらのデザインはメソッドを過度に公開する傾向があるため、インターフェイスまたはユーティリティクラスを使用して一般的なコードを処理し、より簡単に分離できます。

依存性注入は、これらの状況を処理する最も一般的な方法の1つです。 ここ はC#での非常に単純な例です。基本的に、基本クラスから直接継承するのではなく、各クラスは代わりに、共通の操作を処理するオブジェクトに依存するインターフェースを実装します。このようにして、単体テストでMyCommonClassではなくMyMockedCommonClassを渡すだけで済みます。これにより、コードをオブジェクト指向で使用することもできます。

2
Ryathal

その一般的な動作が頻繁に使用される場合、各サブクラスのテスト中に実行されることは確かですが、本番環境の実行時にも同様に実行されます。何も問題はありません。ただし、これは、一般的なロジックを複数回testする必要があるという意味ではありません。

コンクリート/抽象スーパークラス

スーパークラスが抽象的でなく、動作がパブリックである場合は、1つのテストが必要です。簡潔でシンプル。子供ごとにテストを繰り返す必要はありません。

私がよく見つけたのは、具体的なスーパークラスではなく、抽象的なスーパークラスがほとんどないということです。抽象クラスに含まれる一般的な動作は、それ自体はテストされませんが、派生クラスのテストの一部としてテストされます。私はそれを問題とは考えていません。プライベートメソッドがパブリックメソッドテストの一部として間接的にテストされるのとよく似ています。

モッキング

スーパークラスに属しているものもあなたに属しているので、基本クラス(またはまったく同じ独自のプライベートメソッドの1つ)から一般的な動作をモックまたはスタブする必要はありません。愚か。

その必要性を感じた場合、おそらくクラスが十分にまとまりがなく、継承を使用する代わりに、その動作を別の無関係なクラスに抽出する必要があります。

3
guillaume31

絶対ナンセンス。

いくつかの考え:

  1. OOPとその原則を真剣に受け止めるべきではありません。多くの場合、機能的または他のソフトウェアパラダイムはより理にかなっています。オブジェクトはクールですが、具体的なコンポーネントなどについて考えることです。

  2. テスト、特にユニットテストは、プロジェクトにサービスを提供し、プロジェクトの設計を検証し、その堅牢性を保証することを目的としています

  3. ほとんどの場合、構成は継承よりも優れています

    ちなみに、この原則はOOPでもうまく機能します。90年代の巨大なOO継承ツリーは、実際にはOOPの悪い解釈にすぎませんでした。 デザインパターンに関する4冊の本

あなたの質問に:

いくつかのクラスが基本的に同じ「もの」であり、それらに多くの共通機能がある場合、同じクラスから継承させる必要があります(もちろん、OOプログラミング言語を使用していると仮定すると... )。

これは決してテスト容易性を制限しません。

親クラスをテストするには、テストのためにサブクラスを作成します(テストクラスの横に配置します)。これはまさに、クラスがどのように使用されることが意図されているか、そしてその設計が何を要求しているかです。

子供をテストするには、2つのことを行うことができます。

  1. 悪いアプローチ。これは、動的言語、C#、およびおそらくJavaにのみ適用されます。夕食クラスとしてポーズをとるShimを作成し、それを子の基本クラスとして使用できます。

  2. スーパークラスがサブクラスの本質の一部であることを受け入れます。 仕様による。夕食クラスで定義された動作に準拠している必要があり、実際に一緒にテストする必要があります。これらの良い例は、戦略およびテンプレートメソッドのデザインパターンです。これで、サブクラスをそのままテストできます。また、基本クラスのテストは過度に行わないようにする必要があります。既に別の場所でテストしたためです。

3
AK_