動作がパラメーターの型に切り替わるメソッドを見るときはいつでも、そのメソッドが実際にメソッドパラメーターに属しているかどうかをすぐに検討します。たとえば、次のようなメソッドの代わりに:
public void sort(List values) {
if (values instanceof LinkedList) {
// do efficient linked list sort
} else { // ArrayList
// do efficient array list sort
}
}
私はこれを行います:
values.sort();
// ...
class ArrayList {
public void sort() {
// do efficient array list sort
}
}
class LinkedList {
public void sort() {
// do efficient linked list sort
}
}
私たちはそれをいつ使うべきかを知っている場所に振る舞います。実装のタイプや詳細を知る必要がない実際の抽象化を作成します。あなたの状況では、このメソッドを元のクラス(私が呼び出すO
)からタイプに移動しA
、タイプでオーバーライドする方が理にかなっている可能性がありますB
。メソッドがdoIt
何らかのオブジェクトで呼び出された場合は、に移動doIt
しA
て、の異なる動作でオーバーライドしますB
。doIt
最初に呼び出された場所からのデータビットがある場合、またはメソッドが十分な場所で使用されている場合は、元のメソッドを残して委任できます。
class O {
int x;
int y;
public void doIt(A a) {
a.doIt(this.x, this.y);
}
}
ただし、もう少し深く潜ることができます。代わりにブール型パラメーターを使用するという提案を見て、同僚の考え方について何がわかるかを見てみましょう。彼の提案はすることです:
public void doIt(A a, boolean isTypeB) {
if (isTypeB) {
// do B stuff
} else {
// do A stuff
}
}
これは、instanceof
最初の例で使用したものと非常によく似ていますが、そのチェックを外部化している点が異なります。つまり、次の2つの方法のいずれかで呼び出す必要があります。
o.doIt(a, a instanceof B);
または:
o.doIt(a, true); //or false
最初の方法では、呼び出しポイントはA
それがどのタイプであるかを知りません。したがって、ブール値をずっと下に渡す必要がありますか?これは、コードベース全体で本当に必要なパターンですか?考慮する必要がある3番目のタイプがある場合はどうなりますか?これがメソッドの呼び出し方法である場合は、それを型に移動し、システムに実装を多態的に選択させる必要があります。
2番目の方法では、呼び出しポイントでのタイプをすでに知っている必要がありa
ます。これは通常、そこでインスタンスを作成するか、そのタイプのインスタンスをパラメーターとして受け取ることを意味します。here O
を取るメソッドを作成するB
と機能します。コンパイラーはどの方法を選択するかを知っています。このような変更を進めている場合、少なくとも実際にどこに向かっているのかがわかるまで、複製は間違った抽象化を作成するよりも優れています。もちろん、この時点で何を変更しても、実際には完了していないことをお勧めします。
との関係をさらに詳しく調べる必要がA
ありB
ます。一般に、継承よりも構成を優先する必要があると言われています。これは、すべての場合には当てはまりませんが、私たちは掘るたら、それは例驚くべき数で真である。B
継承からA
、私たちは信じていることを意味しB
ていますA
。動作が少し異なることを除いて、とB
同じようA
に使用する必要があります。しかし、それらの違いは何ですか?違いにもっと具体的な名前を付けることはできますか?それはでB
はありませんが、A
本当にA
ありX
ますA'
かB'
?そうした場合、コードはどのように見えるでしょうか?
A
前述のようにメソッドをに移動した場合、X
intoのインスタンスを注入し、A
そのメソッドをに委任できますX
。
class A {
X x;
A(X x) {
this.x = x;
}
public void doIt(int x, int y) {
x.doIt(x, y);
}
}
とを実装A'
してB'
、を取り除くことができますB
。より暗黙的な概念に名前を付けてコードを改善し、コンパイル時ではなく実行時に自分でその動作を設定できるようにしました。A
実際には、抽象度も低くなっています。拡張された継承関係の代わりに、委任されたオブジェクトのメソッドを呼び出します。そのオブジェクトは抽象的ですが、実装の違いにのみ焦点を当てています。
ただし、最後に確認する必要があります。同僚の提案に戻りましょう。すべての呼び出しサイトで自分のタイプを明示的に知ってA
いる場合は、次のような呼び出しを行う必要があります。
B b = new B();
o.doIt(b, true);
以前に、またはのいずれかでA
あるX
を作成するときに想定しました。しかし、この仮定でさえ正しくないかもしれません。これは、この差の唯一の場所であるとの問題?もしそうなら、多分少し異なるアプローチを取ることができます。orのいずれかがまだありますが、には属していません。それだけを気にするので、それだけに渡してみましょう:A'
B'
A
B
X
A'
B'
A
O.doIt
O.doIt
class O {
int x;
int y;
public void doIt(A a, X x) {
x.doIt(a, x, y);
}
}
これで、呼び出しサイトは次のようになります。
A a = new A();
o.doIt(a, new B'());
もう一度、B
消え、抽象化はより焦点を絞ったに移りX
ます。しかし、今回は、A
知識が少ないため、さらに単純になります。それは抽象的なものではありません。
コードベースでの重複を減らすことが重要ですが、重複が最初に発生する理由を考慮する必要があります。重複は、抜け出そうとしているより深い抽象化の兆候である可能性があります。