Java複合は継承より優先されます
16203 ワード
コンポジットが継承より優れている
継承はカプセル化を破った(サブクラス依存親の特定の機能の実装の詳細)
合理的な使用継承の状況:パッケージ内で を使用親クラスは継承のために設計され、is-a関係 が存在するというドキュメントの説明がある.
継承は、子が本当に親の子タイプである場合にのみ適用されます.
2つのクラスAとBについては、両者の間に「is-a」関係が存在する限り、クラスBはクラスAを拡張することができる.
継承メカニズムは、親API内のすべての欠陥をサブクラスに伝播し、複合は、これらの欠陥を隠すために新しいAPIを設計することを可能にする.
複合(composition):既存のクラスを拡張するのではなく、新しいクラスにプライベートドメインを追加し、既存のクラスのインスタンスを参照します.
転送(fowarding):新しいクラスの各インスタンスメソッドは、含まれる既存のクラスインスタンスに対応するメソッドを呼び出し、結果を返すことができます.
上記の例では、FowardingSetは転送クラスであり、被包装クラスでもあるが、InstrumentedSetは依頼モードではなく装飾者モードを採用した包装クラスである.
デコレーションモード:
装飾者モードは組み合わせに似ていて、任意に組み合わせたり、制定したりすることができます.新しいニーズがあるときは、アクセサリーを追加してOKです.必要に応じてコンポーネントを追加することで、既存のコードを変更することなく新しい機能を拡張したり修正したりすることができるようになります.それともその設計原則--open for extension,close for modification.
動的に対象に責任を付加する.機能を拡張するには、装飾者は継承よりも弾力性のある代替案を提供します.装飾者と被装飾者の間には同じタイプ、すなわち共通のスーパークラスが必要である.ここで継承を適用するのは、実装方法のレプリケーションではなく、実装タイプのマッチングです.装飾者と被装飾者は同じタイプであるため、装飾者が被装飾者に取って代わることができ、被装飾者に装飾者独自の行為を持たせることができる.装飾者モデルの理念に基づいて、私たちはいつでも、新しい装飾者が新しい行為を増やすことを実現することができます.継承であれば、新しい動作を追加するたびに、元のプログラムを変更します.
できるだけ機能を独立に分割してデカップリングし、それぞれの新しい機能を分離して装飾者と呼ばれ、必要に応じて組み合わせて使用することができ、単独で継承を使用するように複数の機能を結合するのではなく、読書と使用が不便であり、拡張を利用しない.例えば、インタフェースAが機能1 2 3 4 5を持っている場合、単に継承を使用すれば、では,構造をオブジェクト向けプログラミングに適合させるためには,10個のサブクラスに組み合わせられ,機能がさらに拡張されると,数が恐ろしくなり,利用者に記憶され理解されにくくなる.デコレーションモードを使用すると、数人のデコレーションを実現し、必要に応じて組み合わせて使用するだけでよい.
パッケージクラスはコールバックフレームワーク(callback framework)で使用するのに適していません.SELFの問題が発生します.
コールバックフレームワークでは、オブジェクトは自身の参照を他のオブジェクトに渡し、後続の呼び出し(コールバック)に使用します.
SELF問題:包装されたオブジェクトは外の包装オブジェクトを知らないので、自分への参照(this)を伝え、コールバック時に外の包装オブジェクトを避けました.
簡単に言えば、継承機能は非常に強力ですが、パッケージの原則に反して多くの問題があります.サブクラスとスーパークラスにサブタイプ関係がある場合にのみ、継承を使用するのが適切であるが、サブクラスとスーパークラスが異なるパッケージにあり、スーパークラスが継承のために設計されていない場合、継承は脆弱性をもたらし、このような脆弱性を回避するために、継承の代わりに適合と転送メカニズムを使用することができ、特に適切なインタフェースが存在してパッケージクラスを実現する場合に使用することができる.包装類はサブ類より丈夫であるだけでなく、機能も強い.
継承と複合の間でどのように選択しますか?抽象的には、子クラスと親クラスが確かに「is-a」関係にある場合にのみ継承を使用し、そうでない場合は複合を使用します.あるいは,比較的現実的な点では,サブクラスがスーパークラスの部分的な挙動を実現するだけであれば,複合を用いることを考慮する.
継承はカプセル化を破った(サブクラス依存親の特定の機能の実装の詳細)
合理的な使用継承の状況:
継承は、子が本当に親の子タイプである場合にのみ適用されます.
2つのクラスAとBについては、両者の間に「is-a」関係が存在する限り、クラスBはクラスAを拡張することができる.
継承メカニズムは、親API内のすべての欠陥をサブクラスに伝播し、複合は、これらの欠陥を隠すために新しいAPIを設計することを可能にする.
複合(composition):既存のクラスを拡張するのではなく、新しいクラスにプライベートドメインを追加し、既存のクラスのインスタンスを参照します.
転送(fowarding):新しいクラスの各インスタンスメソッドは、含まれる既存のクラスインスタンスに対応するメソッドを呼び出し、結果を返すことができます.
1 public class FowardSet implements Set { # ,
2
3 // ,
4 private final Set set;
5
6 public FowardSet(Set set){
7 this.set = set;
8 }
9
10
11 /*
12 *
13 */
14 @Override
15 public int size() {
16 return set.size();
17 }
18
19 @Override
20 public boolean isEmpty() {
21 return set.isEmpty();
22 }
23
24 @Override
25 public boolean contains(Object o) {
26 return set.contains(o);
27 }
28
29 @NotNull
30 @Override
31 public Iterator iterator() {
32 return set.iterator();
33 }
34
35 @NotNull
36 @Override
37 public Object[] toArray() {
38 return set.toArray();
39 }
40
41 @NotNull
42 @Override
43 public T[] toArray(T[] a) {
44 return set.toArray(a);
45 }
46
47 @Override
48 public boolean add(E e) {
49 return set.add(e);
50 }
51
52 @Override
53 public boolean remove(Object o) {
54 return set.remove(o);
55 }
56
57 @Override
58 public boolean containsAll(Collection> c) {
59 return set.containsAll(c);
60 }
61
62 @Override
63 public boolean addAll(Collection extends E> c) {
64 return set.addAll(c);
65 }
66
67 @Override
68 public boolean retainAll(Collection> c) {
69 return set.retainAll(c);
70 }
71
72 @Override
73 public boolean removeAll(Collection> c) {
74 return set.removeAll(c);
75 }
76
77 @Override
78 public void clear() {
79 set.clear();
80 }
81
82 @Override
83 public boolean equals(Object obj) {
84 return set.equals(obj);
85 }
86
87 @Override
88 public String toString() {
89 return set.toString();
90 }
91
92 @Override
93 public int hashCode() {
94 return set.hashCode();
95 }
96 }
97
98 /*
99 * (wrapper class),
100 */
101 public class InstrumentedSet extends FowardSet {
102 private int addCount=0;
103
104 public InstrumentedSet(Set set) {
105 super(set);
106 }
107
108 @Override
109 public boolean add(E e) {
110 addCount++;
111 return super.add(e);
112 }
113
114 @Override
115 public boolean addAll(Collection extends E> c) {
116 addCount+=c.size();
117 return super.addAll(c);
118 }
119
120 public int getAddCount() {
121 return addCount;
122 }
123 }
上記の例では、FowardingSetは転送クラスであり、被包装クラスでもあるが、InstrumentedSetは依頼モードではなく装飾者モードを採用した包装クラスである.
デコレーションモード:
装飾者モードは組み合わせに似ていて、任意に組み合わせたり、制定したりすることができます.新しいニーズがあるときは、アクセサリーを追加してOKです.必要に応じてコンポーネントを追加することで、既存のコードを変更することなく新しい機能を拡張したり修正したりすることができるようになります.それともその設計原則--open for extension,close for modification.
動的に対象に責任を付加する.機能を拡張するには、装飾者は継承よりも弾力性のある代替案を提供します.装飾者と被装飾者の間には同じタイプ、すなわち共通のスーパークラスが必要である.ここで継承を適用するのは、実装方法のレプリケーションではなく、実装タイプのマッチングです.装飾者と被装飾者は同じタイプであるため、装飾者が被装飾者に取って代わることができ、被装飾者に装飾者独自の行為を持たせることができる.装飾者モデルの理念に基づいて、私たちはいつでも、新しい装飾者が新しい行為を増やすことを実現することができます.継承であれば、新しい動作を追加するたびに、元のプログラムを変更します.
できるだけ機能を独立に分割してデカップリングし、それぞれの新しい機能を分離して装飾者と呼ばれ、必要に応じて組み合わせて使用することができ、単独で継承を使用するように複数の機能を結合するのではなく、読書と使用が不便であり、拡張を利用しない.例えば、インタフェースAが機能1 2 3 4 5を持っている場合、単に継承を使用すれば、では,構造をオブジェクト向けプログラミングに適合させるためには,10個のサブクラスに組み合わせられ,機能がさらに拡張されると,数が恐ろしくなり,利用者に記憶され理解されにくくなる.デコレーションモードを使用すると、数人のデコレーションを実現し、必要に応じて組み合わせて使用するだけでよい.
パッケージクラスはコールバックフレームワーク(callback framework)で使用するのに適していません.SELFの問題が発生します.
コールバックフレームワークでは、オブジェクトは自身の参照を他のオブジェクトに渡し、後続の呼び出し(コールバック)に使用します.
SELF問題:包装されたオブジェクトは外の包装オブジェクトを知らないので、自分への参照(this)を伝え、コールバック時に外の包装オブジェクトを避けました.
簡単に言えば、継承機能は非常に強力ですが、パッケージの原則に反して多くの問題があります.サブクラスとスーパークラスにサブタイプ関係がある場合にのみ、継承を使用するのが適切であるが、サブクラスとスーパークラスが異なるパッケージにあり、スーパークラスが継承のために設計されていない場合、継承は脆弱性をもたらし、このような脆弱性を回避するために、継承の代わりに適合と転送メカニズムを使用することができ、特に適切なインタフェースが存在してパッケージクラスを実現する場合に使用することができる.包装類はサブ類より丈夫であるだけでなく、機能も強い.
継承と複合の間でどのように選択しますか?抽象的には、子クラスと親クラスが確かに「is-a」関係にある場合にのみ継承を使用し、そうでない場合は複合を使用します.あるいは,比較的現実的な点では,サブクラスがスーパークラスの部分的な挙動を実現するだけであれば,複合を用いることを考慮する.