継承と集約

5484 ワード

一:継承:
継承の概念:子クラスは、構造関数、構造関数以外の親クラスのすべてのメンバーを継承することができるが、使用できるかどうかは継承方式によって制限される.
継承、表現は「is-a」の関係であり、両者の間には上下の関係があります.例えば、私たちは「human is a animal」(人間は動物の一種、言い換えれば動物は人間の上司)という関係を表現するには、継承calss animal{};class human:public animal//継承{};継承カテゴリ:1.公有継承:class A:public B{}親クラスの共有メンバーと保護メンバーが継承されるときは変わらない.親クラスのプライベートメンバーは継承されますが、子クラスでは表示されません.
2.プライベート継承:class A:private B{}またはclass A:B{}親の共有メンバーおよび保護メンバーがプライベートに継承する.親クラスのプライベートメンバーは継承するが、子クラスでは表示されない.
3.保護継承:class A:protected B{}親の共有メンバーと保護メンバーが保護されます.親クラスのプライベートメンバーは継承するが、子クラスでは表示されない.
まとめ:(1).いずれの継承方式においても、親クラスのプライベートメンバーは、本クラスのメンバー関数のみでアクセスでき、クラス外(子クラスを含む)はアクセスできない.  (2).保護メンバーの存在の必要性:クラスを設計する際に継承する可能性を考慮し、子クラスが親クラスの非表示メンバーにアクセスできるようにするために設立された.保護メンバーとプライベートメンバーは、本クラスにとって非表示メンバーの機能を実現することができ、本クラス以外はアクセスできないが、保護メンバーはサブクラスの使用を可能にする.
二:C++のクラスの組合せ(UML)と集約:
C++自体は組合せの概念はなく,組合せはUMLから来ている.組合せと集約は、2つの平等なオブジェクト間の全体と局所の関係を表すが、この関係の緊密性の違いによって、組合せと集約に分けられる.コンビネーションは「contain-a」の関係を表し、全体と局所は同じライフサイクルを有し、言い換えれば、全体のオブジェクトが存在しなければ、局所オブジェクトも消滅する.C++では、通常、クラス全体にローカルクラスのオブジェクトをメンバーとして追加します.このような組み合わせ関係(全体オブジェクトが作成されると同時に、局所オブジェクトも作成され、全体オブジェクトが消失し、局所オブジェクトも消失する)を表現します.例えば、私たちは「human conains a head」(人には頭があり、人は生きていて、頭も生きていて、人が死んだら、頭はそうです)、class head{};class human{private:head;//データメンバーオブジェクトが表す組合せ関係}同じ表を集約するのは全体と局所の関係ですが、この関係はずっと緩やかで、「has-a」の関係を表しています.全体と局所はそれぞれのライフサイクルを持っており、両者は単独で存在することができ、互いに独立しており、組合せのように両者は依存していません.しかし、両者を集約して、新しいタイプを形成することもできます.たとえば、私たちは「ヒューマンハスa computer」と言います.C++では、通常、クラス全体に局所クラスを指すポインタを入れて、この「集約」関係を表現します.(全体オブジェクトを作成するときは、必ずしもローカルオブジェクトがあるとは限らず、すでに作成されているかもしれないし、まだ作成されていないかもしれないが、全体オブジェクトが消えているかもしれないし、ローカルオブジェクトが引き続き存在しているかもしれないし、消えているかもしれないし、コンピュータと人の関係を想像して、生は持ってこないし、死は持って行かない).class computer{};class human{///...private:computer*pCom;//ポインタが表す集約関係};まとめると、継承表現は上下関係(is-a)であり、集約表現は緩やかな全体と局所関係(has-a)であり、組み合わせ表現は緊密な全体局所関係(contain-a)である.
継承(Is-A)ですか、重合(Has-a)ですか.これは問題です.
 
public class Stack extends LinkedList
{  public void push(Object object)
{//スタック}
 public Object peek()
{//スタックトップ要素return nullを取得する; }
 public boolean isEmpty()
{//スタックが空のreturn falseであるかどうかを判断する. }
 public Object pop()
//弾桟return null;}
このように対応する実装コードは簡単です.
public class Stack extends LinkedList
{  public void push(Object object)
//スタックadd(0,object); }
 public Object peek()
{//スタックトップ要素if(isEmpty(){return null;  }   return get(0);  }
 public boolean isEmpty()
{//スタックが空のreturn size()=0であるかどうかを判断します. }
 public Object pop()
//弾桟if(isEmpty()
{    return null;   }   return remove(0);  } }
しかし、このようなデザインは合理的ですか?継承と集約に直面してどのように選択するか分からないとき、どのように困難な決定を下すのでしょうか.
継承はIs-a関係であり、つまり、設計するクラスBが意味的にAクラスと言える場合、Cat(猫)がAnimals(動物)である場合、Cat extends Animalsを継承することができる.集約はHas-a(含む)関係であり、Aが設計するBクラスが意味的に別のクラスを含む場合、また、AがBの中で全体的な機能である役割を果たしている場合、AクラスはBクラスの外で透明であることが多いことに注意してください.集約の使用を検討します.例えば、1匹の猫(Cat)に目(Eyes)がある場合、CatクラスにはEyesクラスが含まれているように設計することができ、AクラスがBクラスの外で透明である理由については、以下のように設計することができる.
public class Cat
{  private Eyes eyes;    public Cat(Eyes eyes)
{   this.eyes = eyes;  }
 public void see()
{   eyes.see();  } }
public class Eyes
 {  public void see()
{   System.out.println("see");  } }
Catクラスオブジェクトを使用する場合、その内部のsee()メソッドの実装に注目する必要はありません.明らかに猫も目ではありません(Is-a関係ではありません)、前の問題を見ると、スタックはチェーンテーブルではないので、継承の方式を採用するのは適切ではありません.継承がIs-a関係であるのは、親のすべての方法、すなわち親のすべての行為を継承しているからです.上の継承実現スタックの方法では、スタックはチェーンテーブルのすべての方法を継承しており、スタックの保証はできませんプロパティ-first in last out(先進後出)です.チェーンテーブルでもあるため、親から継承するメソッドを呼び出して任意の場所で削除および挿入できます.次は、より良いhas-a実装方法です.
public class Stack
 {  private List list;    public void push(Object object)
//スタックlist.add(0,object); }
 public Object peek()
//スタックトップ要素if(isEmpty()の取得
 {    return null;   }   return list.get(0);  }
 public boolean isEmpty()
{//スタックが空のreturn list.size()=0であるかどうかを判断します. }
 public Object pop()
//弾桟if(isEmpty()
{    return null;   }   return list.remove(0);  } }
 
このときスタックの対外的な表現は純粋にスタックであり、スタックの行為しか持っていないからである.中がどのように実現されているのかについては、ユーザーは関心を持つ必要はなく、同時に安全であり、ユーザーはスタックに入る以外に、他の削除挿入操作をすることはできない.ここではリストをその属性として選択し、ユーザーがArrayListやLinkedListを使用することを制限していないが、実際にはCollection属性を使用することができ、ユーザーの選択はさらに大きくなる.もちろん配列も使えます.外部表現が一致するスタックは、その内部実現が多様化できる、すなわちhas-a関係においてA類がB類の外で透明であることがわかる.リストを使用して実装する場合は、リストの最後の要素を取り出すたびに、要素をスタックに入れるときにリストの最後の位置に追加するなど、上記とは異なるポリシーを採用することもできます.
今は難しい決定を下すことができます.簡単に言えば、BがAだと意味的に言えるなら、継承を採用し、そうでなければ集約を使用します.集約して使う場合が多いです.
 
継承もhas-a、重合もIs-a
BクラスがAクラスを継承している場合、BクラスはAクラスのすべての行為を持っていることは間違いなく、論理的にはBクラスは実はHas Aである.superキーワードはクラスの親オブジェクト参照を表し、継承がhas-aの関係であると論理的に誤りがないことがわかります.なぜhas-a関係を完全に継承できないのか.これは,継承時に必要な行為を継承する際に不要な行為,さらには出現できない行為が,上記のように文鎖表の任意の位置の挿入削除行為も継承されるためである.JAvaには継承方法が1つしかありません.これはjavaの設計哲学「C+--」に由来します.C++にはより多様な継承方式があり、そのうちの1つの継承はIs-a関係ではなく完全なhas-a関係である--私有継承である.Bクラスのプライベート継承AクラスからAクラスのすべての行為をすべてBクラス自身のプライベート行為に継承することで、Bクラスは二度とAクラスの行為を表現することができず、完全なhas-a関係である.
もちろんhas-a関係もIs-aの関係を体現することができて、これはユーザーの設計から来て、B類の中ですべてのA類の方法を定義することができて、それから内部はA類の実例を呼び出して実現して、このようにB類は対外的にA類のすべての行為を表現することができます.もちろんAを持つ同名の方法は必ずしもBとして表現できるとは限らない.あなたが同時にAを継承しない限り、待って......少し乱れているのではないでしょうか.
BクラスはAクラスを継承し、BクラスはAクラスのインスタンス(A a=new A()も含む.これでBには2つのAのインスタンスがあるわけではない.1つはsuper、1つはaである.確かにそうである.しかし、Aがインタフェースであれば?
BはインタフェースAを実装し、BクラスにはAを実装するクラスのもう1つの例がある.
このようにBは対外的にAとして使用することができて、内にaを呼び出す方法に対して実際の操作を行って、Bは何もしていないようですか?何もしていないのではなく、Bがエージェントをしています.これが私たちがよく言っているエージェントモデルです.