web-dev-qa-db-ja.com

議論の数を低く保ち、サードパーティの依存関係を別々に保つにはどうすればよいですか?

サードパーティのライブラリを使用しています。彼らは私に [〜#〜] pojo [〜#〜] を渡します、それは私たちの意図と目的のために、おそらく次のように実装されています:

public class OurData {
  private String foo;
  private String bar;
  private String baz;
  private String quux;
  // A lot more than this

  // IMPORTANT: NOTE THAT THIS IS A PACKAGE PRIVATE CONSTRUCTOR
  OurData(/* I don't know what they do */) {
    // some stuff
  }

  public String getFoo() {
    return foo;
  }

  // etc.
}

APIのカプセル化や単体テストの促進など、さまざまな理由で、データをラップしたいと思います。しかし、コアクラスがそれらのデータに依存することは望まない(ここでも、テスト上の理由から)!だから今私はこのようなものを持っています:

public class DataTypeOne implements DataInterface {
  private String foo;
  private int bar;
  private double baz;

  public DataTypeOne(String foo, int bar, double baz) {
    this.foo = foo;
    this.bar = bar;
    this.baz = baz;
  }
}

public class DataTypeTwo implements DataInterface {
  private String foo;
  private int bar;
  private double baz;

  public DataTypeOne(String foo, int bar, double baz, String quux) {
    this.foo = foo;
    this.bar = bar;
    this.baz = baz;
    this.quux = quux;
  }
}

そしてこれ:

public class ThirdPartyAdapter {
  public static makeMyData(OurData data) {
    if(data.getQuux() == null) {
      return new DataTypeOne(
        data.getFoo(),
        Integer.parseInt(data.getBar()),
        Double.parseDouble(data.getBaz()),
      );
    } else {
      return new DataTypeTwo(
        data.getFoo(),
        Integer.parseInt(data.getBar()),
        Double.parseDouble(data.getBaz()),
        data.getQuux();
      );
  }
}

このアダプタークラスは、サードパーティのAPIについて認識しなければならない他のいくつかのクラスと結合され、システムの残りの部分への浸透を制限します。しかし...この解決策は素晴らしいです!クリーンコード、40ページ:

4つ以上の引数(ポリアディック)は、非常に特別な正当化を必要とします-とにかく使用しないでください。

私が検討したこと:

  • 静的ヘルパーメソッドではなくファクトリオブジェクトを作成する
    • 数え切れないほどの議論があるという問題を解決しない
  • 依存コンストラクターを持つDataTypeOneおよびDataTypeTwoのサブクラスを作成する
    • まだポリアドの保護されたコンストラクタがあります
  • 同じインターフェースに準拠する完全に別個の実装を作成する
  • 上記のアイデアの複数を同時に

この状況にどのように対処する必要がありますか?


これは 破損防止レイヤー の状況ではないことに注意してください。 APIに問題はありません。問題は次のとおりです。

  • MYデータ構造にimport com.third.party.library.SomeDataStructure;を使用したくない
  • テストケースでデータ構造を構築できません
  • 現在の解決策では、非常に多くの引数が発生します。データ構造を渡さずに、引数の数を少なくしたい。
  • その質問は、「は腐敗防止層ですか?」です。私の質問は「どうやってこのシナリオを解決するためにパターン、任意のパターンを使用できますか?」

私もコードを求めているのではありません(そうでない場合、この質問はSOにあります)。コードを効果的に記述できるように十分な答えを求めているだけです(その質問は提供していません)。

13
durron597

いくつかの初期化パラメーターがある場合に使用した戦略は、初期化用のパラメーターのみを含む型を作成することです

public class DataTypeTwoParameters {
    public String foo;  // use getters/setters instead if it's appropriate
    public int bar;
    public double baz;
    public String quuz;
}

次に、DataTypeTwoのコンストラクターがDataTypeTwoParametersオブジェクトを受け取り、DataTypeTwoが次のように構築されます。

DataTypeTwoParameters p = new DataTypeTwoParameters();
p.foo = "Hello";
p.bar = 4;
p.baz = 3;
p.quuz = "World";

DataTypeTwo dtt = new DataTypeTwo(p);

これは、DataTypeTwoに入るすべてのパラメーターが何であるか、およびそれらが何を意味するかを明確にする多くの機会を与えます。 DataTypeTwoParametersコンストラクターで適切なデフォルトを提供して、APIのコンシューマーが好きな順序で設定する必要のある値のみを実行できるようにすることもできます。

10
Erik

ここで本当に2つの個別の懸念事項があります。APIをラップすることと、引数の数を少なく保つことです。

APIをラップするときの考え方は、要件以外に何も知らずに、インターフェースをゼロから設計することです。あなたは彼らのAPIに何も問題がないと言います、そして同じ呼吸のリストで彼らのAPIに多くの問題があります:テスト可能性、構築可能性、1つのオブジェクトのパラメータが多すぎるなど。APIを書きます欲しいあなたが持っていた。 1つではなく複数のオブジェクトが必要な場合は、それを実行します。 POJOをcreateするオブジェクトに対して、1レベル上のラップが必要な場合は、それを行います。

次に、目的のAPIを取得したら、パラメーター数は問題ではなくなる可能性があります。もしそうなら、考慮すべき多くの一般的なパターンがあります:

  • Erikの回答 のようなパラメーターオブジェクト。
  • ビルダーパターン 。個別のビルダーオブジェクトを作成し、いくつかのセッターを呼び出してパラメーターを個別に設定してから、最終オブジェクトを作成します。
  • プロトタイプパターン 。フィールドがすでに内部的に設定されている目的のオブジェクトのサブクラスを複製します。
  • すでにおなじみのファクトリー。
  • 上記のいくつかの組み合わせ。

これらの作成パターンは、多くの場合、ポリアドコンストラクタを呼び出すことになることに注意してください。これは、カプセル化されている場合は問題ないと見なす必要があります。ポリアドコンストラクタの問題は、それらを1回呼び出すのではなく、オブジェクトを構築する必要があるたびに強制的に呼び出す必要がある場合です。

通常は、内部を再実装するよりも、OurDataオブジェクトへの参照を保存してメソッド呼び出しを転送することで、基になるAPIにパススルーする方がはるかに簡単で保守しやすいことに注意してください。例えば:

public class DataTypeTwo implements DataInterface {
  private OurData data;

  public DataTypeOne(OurData data) {
    this.data = data;
  }

   public String getFoo() {
    return data.getFoo();
  }

  public int getBar() {
    return Integer.parseInt(data.getBar());
  }
  ...
}
14
Karl Bielefeldt

ボブおじさんの勧めを厳格に解釈しすぎているのではないかと思います。通常のクラスの場合、ロジックとメソッド、コンストラクターなどを使用すると、ポリアドコンストラクターは実際にコードの匂いによく似ています。しかし、フィールドを公開する厳密なデータコンテナーであり、本質的には既にFactoryオブジェクトであるものによって生成されたものについては、それほど悪くないと思います。

コメントcanパラメータオブジェクトパターンを使用して、コメントで提案されているように、これらのコンストラクタパラメータをラップできます。ローカルデータタイプラッパーとはalready本質的にはパラメータオブジェクト。パラメーターオブジェクトが実行するのは、パラメーターをパックし(どのように作成しますか?ポリアドコンストラクターを使用して?)、次にそれらをほぼ同じオブジェクトに1秒後にアンパックすることです。

フィールドのセッターを公開して呼び出したくない場合は、明確に定義されカプセル化されたファクトリ内のポリアドコンストラクターを使用するのが適切だと思います。