サードパーティのライブラリを使用しています。彼らは私に [〜#〜] 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つ以上の引数(ポリアディック)は、非常に特別な正当化を必要とします-とにかく使用しないでください。
私が検討したこと:
この状況にどのように対処する必要がありますか?
これは 破損防止レイヤー の状況ではないことに注意してください。 APIに問題はありません。問題は次のとおりです。
import com.third.party.library.SomeDataStructure;
を使用したくない私もコードを求めているのではありません(そうでない場合、この質問はSOにあります)。コードを効果的に記述できるように十分な答えを求めているだけです(その質問は提供していません)。
いくつかの初期化パラメーターがある場合に使用した戦略は、初期化用のパラメーターのみを含む型を作成することです
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のコンシューマーが好きな順序で設定する必要のある値のみを実行できるようにすることもできます。
ここで本当に2つの個別の懸念事項があります。APIをラップすることと、引数の数を少なく保つことです。
APIをラップするときの考え方は、要件以外に何も知らずに、インターフェースをゼロから設計することです。あなたは彼らのAPIに何も問題がないと言います、そして同じ呼吸のリストで彼らのAPIに多くの問題があります:テスト可能性、構築可能性、1つのオブジェクトのパラメータが多すぎるなど。APIを書きます欲しいあなたが持っていた。 1つではなく複数のオブジェクトが必要な場合は、それを実行します。 POJOをcreateするオブジェクトに対して、1レベル上のラップが必要な場合は、それを行います。
次に、目的のAPIを取得したら、パラメーター数は問題ではなくなる可能性があります。もしそうなら、考慮すべき多くの一般的なパターンがあります:
これらの作成パターンは、多くの場合、ポリアドコンストラクタを呼び出すことになることに注意してください。これは、カプセル化されている場合は問題ないと見なす必要があります。ポリアドコンストラクタの問題は、それらを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());
}
...
}
ボブおじさんの勧めを厳格に解釈しすぎているのではないかと思います。通常のクラスの場合、ロジックとメソッド、コンストラクターなどを使用すると、ポリアドコンストラクターは実際にコードの匂いによく似ています。しかし、フィールドを公開する厳密なデータコンテナーであり、本質的には既にFactoryオブジェクトであるものによって生成されたものについては、それほど悪くないと思います。
コメントcanパラメータオブジェクトパターンを使用して、コメントで提案されているように、これらのコンストラクタパラメータをラップできます。ローカルデータタイプラッパーとはalready本質的にはパラメータオブジェクト。パラメーターオブジェクトが実行するのは、パラメーターをパックし(どのように作成しますか?ポリアドコンストラクターを使用して?)、次にそれらをほぼ同じオブジェクトに1秒後にアンパックすることです。
フィールドのセッターを公開して呼び出したくない場合は、明確に定義されカプセル化されたファクトリ内のポリアドコンストラクターを使用するのが適切だと思います。