web-dev-qa-db-ja.com

「効用関数」クラスを使いこなす

Javaコードベースでは、次のパターンが見られます。

_/**
 This is a stateless utility class
 that groups useful foo-related operations, often with side effects.
*/
public class FooUtil {
  public int foo(...) {...}
  public void bar(...) {...}
}

/**
 This class does applied foo-related things.
*/
class FooSomething {
  int DoBusinessWithFoo(FooUtil fooUtil, ...) {
    if (fooUtil.foo(...)) fooUtil.bar(...);
  }
}
_

私が気になるのは、テストのため、どこにでもFooUtilのインスタンスを渡さなければならないことです。

  • FooUtilのメソッドを静的にすることはできません。FooUtilとそのクライアントクラスの両方をテストするためにそれらをモックすることができないためです。
  • FooUtilを使用する場所でnewのインスタンスを作成することはできません。これも、テストのためにモックすることができないためです。

私の最善の策は注射を使用することです(そして私はそうします)が、それはそれ自身の一連の手間を追加します。また、いくつかのutilインスタンスを渡すと、メソッドパラメータリストのサイズが大きくなります。

これを私が見ないよりうまく処理する方法はありますか?

pdate:ユーティリティクラスはステートレスであるため、静的シングルトンINSTANCEメンバーまたは静的getInstance()メソッドを追加しながら、テスト用のクラスの基礎となる静的フィールド。あまりきれいじゃないようです。

15
9000

まず最初に、さまざまな問題に対してさまざまなプログラミング手法があることを述べさせてください。私の仕事では、私は強力なOO組織化と保守のための設計を好んでおり、私の答えはこれを反映しています。

ほとんどのユーティリティクラスは、メソッドが存在するはずのオブジェクトが存在しないために作成される傾向があります。これはほとんどの場合、誰かがコレクションを渡すか、誰かがBeanを渡すかの2つのケースのいずれかから生じます(これらはしばしばPOJOと呼ばれますが、セッターとゲッターの束はBeanとして最もよく説明されていると思います).

渡すコレクションがある場合、そのコレクションの一部になりたいコードが必要です。これにより、システム全体に散らばり、最終的にユーティリティクラスに収集されます。

私の提案は、コレクション(またはBean)を他の密接に関連するデータで新しいクラスにラップし、「ユーティリティ」コードを実際のオブジェクトに配置することです。

コレクションを拡張してそこにメソッドを追加することはできますが、その方法でオブジェクトの状態を制御できなくなります。それを完全にカプセル化し、必要なビジネスロジックメソッドのみを公開することをお勧めします(「オブジェクトにデータを要求しないでください、オブジェクトにコマンドを与えて、プライベートデータに基づいて動作させる」、重要なOOテナントであり、これについて考えると、ここで提案するアプローチが必要になります)。

この「ラッピング」は、データを保持するがコードを追加できない汎用ライブラリオブジェクトに役立ちます。操作するデータを含むコードを配置することは、OO設計のもう1つの主要な概念です。

このラッピングの概念が悪いアイデアとなるコーディングスタイルはたくさんあります。1回限りのスクリプトまたはテストを実装するだけでクラス全体を書きたいと思う人はいますか?しかし、OOには、コードを自分で追加できない基本的なタイプ/コレクションのカプセル化が必須だと思います。

16
Bill K

プロバイダーパターンを使用して、FooSomethingFooUtilProviderオブジェクトを注入できます(コンストラクター内にある可能性があります。1回だけ)。

FooUtilProviderには、FooUtils get();という1つのメソッドしかありません。次に、クラスは提供するFooUtilsインスタンスを使用します。

これで、ディペンデンシーインジェクションフレームワークの使用から1ステップ離れたところにあります。つまり、プロダクションコード用とテストスイート用に、DI cointainerを2回配線するだけで済みます。 FooUtilsインターフェースをRealFooUtilsまたはMockedFooUtilsにバインドするだけで、残りは自動的に行われます。 FooUtilsProviderに依存するすべてのオブジェクトは、適切なバージョンのFooUtilsを取得します。

1
Konrad Morawski