構成と継承は同じですか?構成パターンを実装したい場合、Javaでそれを行うにはどうすればよいですか?
構成と継承は同じですか?構成パターンを実装したい場合、Javaでそれを行うにはどうすればよいですか?
回答:
それらは完全に異なります。継承は「is-a」関係です。作曲は「はさみ」です。
C
を拡張するのではなく、クラスのフィールドとして別のクラスのインスタンスを持つことによって、構成を行いますC
。構成が継承よりもはるかに優れていた良い例はjava.util.Stack
、現在拡張されていjava.util.Vector
ます。これは失敗と見なされます。スタックの「is-NOT-a」ベクトル。要素を勝手に挿入したり削除したりしてはいけません。代わりに作曲しているはずです。
残念ながら、継承階層を変更すると既存のコードとの互換性が失われるため、この設計ミスを修正するのは遅すぎます。持っていたStack
使用される組成物の代わりに継承し、常にAPIに違反することなく、他のデータ構造を使用するように変更することができます。
Josh Blochの著書「Effective Java 2nd Edition」を強くお勧めします
優れたオブジェクト指向設計は、既存のクラスを自由に拡張することではありません。あなたの最初の本能は、代わりに作曲することです。
以下も参照してください。
構成はHAS A
継承を意味するIS A
Example
:車にはエンジンがあり、車は自動車です
プログラミングでは、これは次のように表されます。
class Engine {} // The Engine class.
class Automobile {} // Automobile class which is parent to Car class.
class Car extends Automobile { // Car is an Automobile, so Car class extends Automobile class.
private Engine engine; // Car has an Engine so, Car class has an instance of Engine class as its member.
}
:-/
type
はTypeのフィールドを持つことができますEnum
継承はどのように危険なのでしょうか?
例を見てみましょう
public class X{
public void do(){
}
}
Public Class Y extends X{
public void work(){
do();
}
}
1)上記のコードで明らかなように、クラスYはクラスXと非常に強い結合を持っています。スーパークラスXで何かが変更されると、Yは劇的に壊れる可能性があります。将来のクラスXが以下のシグネチャで機能するメソッドを実装すると仮定
public int work(){
}
変更はクラスXで行われますが、クラスYをコンパイルできなくなります。したがって、この種の依存関係はあらゆるレベルに及ぶ可能性があり、非常に危険な場合があります。スーパークラスのすべてのサブクラス内のコードに対する完全な可視性が常にない場合があり、サブクラスは常にスーパークラスで何が起こっているのかを認識し続ける場合があります。したがって、この強力で不要な結合を回避する必要があります。
コンポジションはこの問題をどのように解決しますか?
同じ例を修正して見てみましょう
public class X{
public void do(){
}
}
Public Class Y{
X x = new X();
public void work(){
x.do();
}
}
ここでは、YクラスにXクラスの参照を作成し、Xクラスのインスタンスを作成してXクラスのメソッドを呼び出しています。これで、その強い結合はすべてなくなりました。スーパークラスとサブクラスは、現在非常に独立しています。クラスは継承状況で危険だった変更を自由に行うことができます。
2)構成の2番目の非常に優れた利点は、メソッド呼び出しの柔軟性を提供することです。次に例を示します。
class X implements R
{}
class Y implements R
{}
public class Test{
R r;
}
r参照を使用するTestクラスでは、XクラスとYクラスのメソッドを呼び出すことができます。この柔軟性は継承にありませんでした
3)別の大きな利点:単体テスト
public class X {
public void do(){
}
}
Public Class Y {
X x = new X();
public void work(){
x.do();
}
}
上記の例では、xインスタンスの状態が不明な場合、いくつかのテストデータを使用して簡単にモックアップでき、すべてのメソッドを簡単にテストできます。インスタンスの状態を取得してメソッドを実行するためにスーパークラスに大きく依存していたため、継承ではこれはまったく不可能でした。
4)継承を避けるべきもう1つの理由は、Javaが多重継承をサポートしていないことです。
これを理解するために例を見てみましょう:
Public class Transaction {
Banking b;
public static void main(String a[])
{
b = new Deposit();
if(b.deposit()){
b = new Credit();
c.credit();
}
}
}
知っておきたいこと:
継承はコンパイル時にその機能を提供する一方で、構成は実行時に簡単に達成されます
構成はHAS-A関係としても知られ、継承はIS-A関係としても知られています
したがって、上記のさまざまな理由から、継承よりも常に構成を優先する習慣をつけてください。
@Michael Rodriguesの回答は正しくないため(申し訳ありません。直接コメントすることはできません)、混乱を招く可能性があります。
インターフェースの実装は継承の一種です ... インターフェースを実装すると、すべての定数を継承するだけでなく、インターフェースで指定された型になるようにオブジェクトをコミットします。それはまだ " is-a "関係です。車がFillableを実装している場合、車は「-」「Fillable」であり、Fillableを使用する場所であればどこでもコードで使用できます。。
構成は継承とは根本的に異なります。合成を使用する場合、他の回答の注記と同様に、継承を使用して作成する「is-a」関係とは対照的に、2つのオブジェクト間に「has-a」関係を作成します。
それで、他の質問の車の例から、もし車が " has-a "ガソリンタンクだと言いたければ、私は次のように構成を使います:
public class Car {
private GasTank myCarsGasTank;
}
うまくいけば、誤解が解消されます。
継承はIS-A関係を引き出します。構成はHAS-A関係を引き出します。戦略パターンは、特定の動作を定義するアルゴリズムのファミリーが存在する場合にコンポジションを使用する必要があることを説明しています。
空飛ぶ行動を実装するアヒルのクラスの古典的な例。
public interface Flyable{
public void fly();
}
public class Duck {
Flyable fly;
public Duck(){
fly = new BackwardFlying();
}
}
したがって、たとえば次のように、flyingを実装する複数のクラスを持つことができます。
public class BackwardFlying implements Flyable{
public void fly(){
Systemout.println("Flies backward ");
}
}
public class FastFlying implements Flyable{
public void fly(){
Systemout.println("Flies 100 miles/sec");
}
}
継承があったとしたら、フライ機能を何度も実装する2つの異なるクラスの鳥がいるでしょう。したがって、継承と構成は完全に異なります。
構成は、そのとおりです。部品を接続することでオブジェクトを作成します。
この回答の残りの部分を編集すると、誤って次の前提に基づいています。
これはインターフェースで実現されます。
たとえば、上の例を使用するとCar
、
Car implements iDrivable, iUsesFuel, iProtectsOccupants
Motorbike implements iDrivable, iUsesFuel, iShortcutThroughTraffic
House implements iProtectsOccupants
Generator implements iUsesFuel
したがって、いくつかの標準的な理論的コンポーネントを使用して、オブジェクトを構築できます。次にHouse
、Car
がその居住者を保護する方法、およびがその居住者を保護する方法を記入するのはあなたの仕事です。
継承はその逆です。完全な(または半完全な)オブジェクトから始めて、変更するさまざまなビットを置き換えるかオーバーライドします。
たとえばMotorVehicle
、Fuelable
メソッドとDrive
メソッドが付属している場合があります。バイクと車を埋めるのは同じなので、FuelメソッドはそのままにしておくことができますがDrive
、バイクの運転方法が非常に異なるため、メソッドをオーバーライドできます。Car
。
継承を使用すると、一部のクラスはすでに完全に実装されており、他のクラスには強制的にオーバーライドするメソッドがあります。構成では何も与えられません。(ただし、何か問題が発生した場合は、他のクラスのメソッドを呼び出すことでインターフェイスを実装できます)。
iUsesFuelなどのメソッドがある場合、車であるかどうかに関係なく、燃料を供給できるオブジェクトを処理することだけを心配するメソッドを別の場所(別のクラス、別のプロジェクト)に置くことができるため、構成はより柔軟に見えます。ボート、ストーブ、バーベキューなど。インターフェイスは、そのインターフェイスを実装していると言うクラスが、そのインターフェイスに関するすべてのメソッドを実際に持つことを義務付けています。例えば、
iFuelable Interface:
void AddSomeFuel()
void UseSomeFuel()
int percentageFull()
その後、あなたはどこか他の方法を持つことができます
private void FillHerUp(iFuelable : objectToFill) {
Do while (objectToFill.percentageFull() <= 100) {
objectToFill.AddSomeFuel();
}
奇妙な例ですが、オブジェクトがを実装しているため、このメソッドはどのように満たされているかを気にしませんiUsesFuel
。物語の終わり。
代わりにInheritanceを使用した場合、継承元のかなり奇妙な「ObjectThatUsesFuel」ベースオブジェクトがない限りFillHerUp
、MotorVehicles
and を処理するために別のメソッドが必要になりますBarbecues
。
ThisCase
でなくで記述されているとされていcamelCase
ます。したがって、インターフェイスに名前を付けることなどが最善IDrivable
です。すべてのインターフェイスをパッケージに正しく再グループ化する場合、「I」は必要ない場合があります。
構成と継承は同じですか?
彼らは同じではありません。
構成:オブジェクトのグループを、オブジェクトの単一のインスタンスと同じ方法で処理する必要があります。コンポジットの目的は、オブジェクトをツリー構造に「構成」して、部分全体の階層を表すことです。
継承:クラスは、直接または間接にかかわらず、すべてのスーパークラスからフィールドとメソッドを継承します。サブクラスは、継承するメソッドをオーバーライドしたり、継承したフィールドやメソッドを非表示にしたりできます。
構成パターンを実装したい場合、Javaでそれを行うにはどうすればよいですか?
ウィキペディアの記事は、Javaで複合パターンを実装するのに十分です。
主要な参加者:
コンポーネント:
葉:
合成:
複合パターンを理解するためのコード例:
import java.util.List;
import java.util.ArrayList;
interface Part{
public double getPrice();
public String getName();
}
class Engine implements Part{
String name;
double price;
public Engine(String name,double price){
this.name = name;
this.price = price;
}
public double getPrice(){
return price;
}
public String getName(){
return name;
}
}
class Trunk implements Part{
String name;
double price;
public Trunk(String name,double price){
this.name = name;
this.price = price;
}
public double getPrice(){
return price;
}
public String getName(){
return name;
}
}
class Body implements Part{
String name;
double price;
public Body(String name,double price){
this.name = name;
this.price = price;
}
public double getPrice(){
return price;
}
public String getName(){
return name;
}
}
class Car implements Part{
List<Part> parts;
String name;
public Car(String name){
this.name = name;
parts = new ArrayList<Part>();
}
public void addPart(Part part){
parts.add(part);
}
public String getName(){
return name;
}
public String getPartNames(){
StringBuilder sb = new StringBuilder();
for ( Part part: parts){
sb.append(part.getName()).append(" ");
}
return sb.toString();
}
public double getPrice(){
double price = 0;
for ( Part part: parts){
price += part.getPrice();
}
return price;
}
}
public class CompositeDemo{
public static void main(String args[]){
Part engine = new Engine("DiselEngine",15000);
Part trunk = new Trunk("Trunk",10000);
Part body = new Body("Body",12000);
Car car = new Car("Innova");
car.addPart(engine);
car.addPart(trunk);
car.addPart(body);
double price = car.getPrice();
System.out.println("Car name:"+car.getName());
System.out.println("Car parts:"+car.getPartNames());
System.out.println("Car price:"+car.getPrice());
}
}
出力:
Car name:Innova
Car parts:DiselEngine Trunk Body
Car price:37000.0
説明:
構成と継承の長所と短所については、以下の質問を参照してください。
単純な単語の集約では、関係があることを意味します。
合成は、集計の特殊なケースです。より具体的には、制限された集約は合成と呼ばれます。オブジェクトに他のオブジェクトが含まれている場合、含まれているオブジェクトがコンテナオブジェクトの存在なしに存在できない場合、それは合成と呼ばれます。 例:クラスに学生が含まれています。学生はクラスなしでは存在できません。クラスと学生の間には構成があります。
集計を使用する理由
コードの再利用性
集計を使用する場合
コードの再利用も、Relationshipがない場合に集約することで実現できます。
継承
継承は親子関係である継承はRelationShipであることを意味する
Javaでの継承は、1つのオブジェクトが親オブジェクトのすべてのプロパティと動作を取得するメカニズムです。
Java 1コードの再利用性における継承の使用。2子クラスとメソッドのオーバーライドに追加機能を追加します(実行時のポリモーフィズムを実現できます)。
継承とコンポジションの両方がコードの再利用性を提供しますが、Javaでのコンポジションと継承の主な違いは、コンポジションがコードを拡張せずに再利用できることですが、継承の場合は、コードまたは機能を再利用するためにクラスを拡張する必要があります。この事実によるもう1つの違いは、Compositionを使用することにより、拡張できない最終クラスでもコードを再利用できるが、継承ではそのような場合にコードを再利用できないことです。また、Compositionを使用すると、メンバー変数として宣言されているため、多くのクラスのコードを再利用できますが、継承では、Javaでは複数の継承がサポートされていないため、Javaでは1つのクラスしか拡張できないため、コードを再利用できます。 。ただし、1つのクラスが複数のクラスを拡張できるため、C ++でこれを行うことができます。ところで、あなたは常にJavaでの継承を超えるポジションを好むその私でさえだけではなく、ジョシュア・ブロックは、彼の本の中で提案しています
この例は、継承と構成の違いを明確に説明していると思います。
この例では、問題は継承と構成を使用して解決されます。著者は以下の事実に注意を払います。継承、スーパークラスの変更は、それを継承すること、派生クラスで問題が発生する可能性があります。
また、UMLを継承または構成に使用した場合の表現の違いも確認できます。
継承と構成。
継承と構成は、クラスの動作の再利用と拡張に使用されます。
IS-A関係タイプなどのファミリーアルゴリズムプログラミングモデルで主に使用される継承は、類似した種類のオブジェクトを意味します。例。
これらはCarファミリーに属しています。
コンポジションはHAS-A関係タイプを表します。これは、Dustreが5つのギアを持っている、Safariが4つのギアを持っているなどのオブジェクトの能力を示します。既存のクラスの能力を拡張する必要があるときはいつでも、コンポジションを使用します。例では、Dusterオブジェクトにギアをもう1つ追加する必要があります。次に、ギアオブジェクトをもう1つ作成して、ダスターオブジェクトに合成する必要があります。
すべての派生クラスがこれらの機能を必要とするまで/ベースクラスを変更しないでください。このシナリオでは、Compositionを使用する必要があります。
クラスBから派生したクラスA
クラスCから派生したクラスA
クラスDから派生したクラスA
クラスAに機能を追加すると、クラスCとDがそれらの機能を必要としない場合でも、すべてのサブクラスで使用できるようになります。このシナリオでは、それらの機能用に別のクラスを作成し、必要なクラスに構成する必要があります(これがクラスBです。
以下に例を示します。
// This is a base class
public abstract class Car
{
//Define prototype
public abstract void color();
public void Gear() {
Console.WriteLine("Car has a four Gear");
}
}
// Here is the use of inheritence
// This Desire class have four gears.
// But we need to add one more gear that is Neutral gear.
public class Desire : Car
{
Neutral obj = null;
public Desire()
{
// Here we are incorporating neutral gear(It is the use of composition).
// Now this class would have five gear.
obj = new Neutral();
obj.NeutralGear();
}
public override void color()
{
Console.WriteLine("This is a white color car");
}
}
// This Safari class have four gears and it is not required the neutral
// gear and hence we don't need to compose here.
public class Safari :Car{
public Safari()
{ }
public override void color()
{
Console.WriteLine("This is a red color car");
}
}
// This class represents the neutral gear and it would be used as a composition.
public class Neutral {
public void NeutralGear() {
Console.WriteLine("This is a Neutral Gear");
}
}
いいえ、どちらも異なります。構成は「HAS-A」の関係に従い、継承は「IS-A」の関係に従います。作曲の最良の例は戦略的パターンでした。