ホームページ  >  記事  >  Java  >  構成と継承

構成と継承

DDD
DDDオリジナル
2024-09-25 11:45:44457ブラウズ

Composición vs Herencia

導入

継承と合成はオブジェクト指向プログラミング (OOP) の 2 つの基本概念ですが、使用方法も目的も異なります。この投稿の目的は、これらの目的と、それらを選択する際に留意すべき点を確認することです。

継承の概念

デザインに伝統を適用することを考えるとき、次のことを理解する必要があります。

  • 定義: 継承では、クラス (派生クラスまたはサブクラスと呼ばれます) は、別のクラス (基本クラスまたはスーパークラスと呼ばれます) からプロパティと動作を継承できます。派生クラスは、基本クラスの機能を拡張または変更できます。
  • 関係: それは「である」(is-a) という関係です。たとえば、クラス「Vehicle」と別のクラス「Car」がある場合、クラス「Car」は「Vehicle」のサブクラスになります。
  • 利点: コードの再利用が促進され、機能を簡単に拡張できます。

構成の概念

一方、オブジェクトを相互に合成することを考えると、次のようになります。

  • 定義: 合成では、オブジェクトは他のオブジェクトを含み、その機能の一部をそれらのオブジェクトに委任します。クラスは継承の代わりに、他のクラスのインスタンスを使用してその機能を実現します。
  • 関係: 「ある」(has-a) 関係です。たとえば、クラス「Engine」とクラス「Car」がある場合、クラス「Car」はクラス「Engine」のオブジェクトを持つことができます。
  • 利点: 柔軟性が向上し、クラス間の結合が少なくなります。一方のクラスの変更は、もう一方のクラスに直接影響しません。

なぜ継承に基づいて合成するのでしょうか?

合成が継承より優れているのか、それともその逆なのかという問題は、ソフトウェア設計において議論の対象となっています。どちらのアプローチにも長所と短所があり、選択は特定のプロジェクトのコンテキストと要件によって異なります。ここでは、継承より合成の方が望ましい例を紹介します。

Java の例を見て、特定の場合に継承よりも合成の方がどのように優先されるかを示してみましょう。オンライン ストアの注文処理システムに取り組んでいるとします。

  • 継承アプローチ:

まず、継承を使用して書籍や電子機器など、購入できるさまざまな種類の製品を表すアプローチを考えてみましょう。

// Clase base para productos
class Producto {
    String nombre;
    double precio;

    Producto(String nombre, double precio) {
        this.nombre = nombre;
        this.precio = precio;
    }

    void procesarPedido() {
        System.out.println("Procesando pedido para " + nombre);
    }
}

// Clase para productos electrónicos que hereda de Producto
class ProductoElectronico extends Producto {
    String modelo;

    ProductoElectronico(String nombre, double precio, String modelo) {
        super(nombre, precio);
        this.modelo = modelo;
    }
}

// Clase para libros que hereda de Producto
class Libro extends Producto {
    String autor;

    Libro(String nombre, double precio, String autor) {
        super(nombre, precio);
        this.autor = autor;
    }
}

このアプローチは機能しますが、新しい製品タイプを導入したり、特定の製品タイプに特定の機能を追加したりする必要がある場合はどうすればよいでしょうか?

  • 構図でフォーカス:

継承に完全に依存する代わりに、合成を使用して、さまざまなタイプの製品をより柔軟に処理できます。

// Clase para productos
class Producto {
    String nombre;
    double precio;

    Producto(String nombre, double precio) {
        this.nombre = nombre;
        this.precio = precio;
    }

    void procesarPedido() {
        System.out.println("Procesando pedido para " + nombre);
    }
}

// Clase para productos electrónicos que utiliza composición
class ProductoElectronico {
    Producto producto;
    String modelo;

    ProductoElectronico(String nombre, double precio, String modelo) {
        this.producto = new Producto(nombre, precio);
        this.modelo = modelo;
    }

    // Puedes agregar lógica específica para productos electrónicos si es necesario
    void procesarPedidoEspecifico() {
        System.out.println("Procesando pedido específico para " + producto.nombre);
    }
}

// Clase para libros que utiliza composición
class Libro {
    Producto producto;
    String autor;

    Libro(String nombre, double precio, String autor) {
        this.producto = new Producto(nombre, precio);
        this.autor = autor;
    }

    // Puedes agregar lógica específica para libros si es necesario
    void procesarPedidoEspecifico() {
        System.out.println("Procesando pedido específico para " + producto.nombre);
    }
}

このアプローチでは、各製品タイプに Product クラスのインスタンスがあり、注文を処理するために共通のロジックを共有できます。さらに、各製品タイプは、processSpecificOrder() などのメソッドを使用して、独自の特定のロジックを持つことができます。この設計はより柔軟であり、継承階層に影響を与えることなく、新しい製品タイプの導入やタイプ固有のロジックの変更が容易になります。

継承をいつ適用するか?

ソフトウェア設計における継承と合成のどちらを選択するかは、対処している問題のコンテキストと特定の要件によって異なります。以下に、合成よりも継承をより適切なオプションとして考慮できる状況をいくつか示します。

  • Relación "es-un": La herencia es especialmente adecuada cuando hay una relación clara de "es-un" entre las clases. Si una clase B es una versión más específica o especializada de una clase A, entonces la herencia tiene sentido. Por ejemplo, si tienes una clase Vehiculo y una clase Automovil, la relación "es-un" es clara, ya que un automóvil es un tipo de vehículo.
class Vehiculo {
    // ...
}

class Automovil extends Vehiculo {
    // ...
}
  • Reutilización de código: La herencia permite la reutilización de código, ya que la clase derivada hereda los miembros y métodos de la clase base. Esto puede ser beneficioso cuando hay una cantidad significativa de código común entre las clases relacionadas.
class Animal {
    void comer() {
        // Lógica común para comer
    }
}

class Perro extends Animal {
    void ladrar() {
        // Lógica específica para ladrar
    }
}
  • Polimorfismo: La herencia es fundamental para la implementación del polimorfismo, que permite a las clases derivadas proporcionar implementaciones específicas de los métodos heredados de la clase base. Esto es útil en escenarios donde necesitas tratar objetos de clases derivadas de manera uniforme.
class Figura {
    void dibujar() {
        // Lógica común para dibujar una figura
    }
}

class Circulo extends Figura {
    void dibujar() {
        // Lógica específica para dibujar un círculo
    }
}

class Cuadrado extends Figura {
    void dibujar() {
        // Lógica específica para dibujar un cuadrado
    }
}

  • Extensiones de funcionalidad: Si estás extendiendo o especializando funcionalidad existente, la herencia puede ser más natural. Por ejemplo, si estás desarrollando una biblioteca y proporcionas una clase base con una funcionalidad básica, los usuarios de tu biblioteca pueden derivar de esa clase base para extender esa funcionalidad.

Que pasa si hacemos una mala herencia?

Si seguimos evaluando los pros y los contras de la herencia, uno de los problemas que puede surgir de una mala herencia es que violaríamos el Principio de Segregación de Interfaces, que indica que los clientes no deberían verse obligados a depender de interfaces que no utilizan. Si una interfaz se extiende de manera que incluya métodos que no son relevantes para todas las implementaciones, los clientes que utilizan esa interfaz podrían verse forzados a implementar o depender de métodos que no necesitan, lo que puede llevar a un diseño menos limpio y más difícil de mantener.

Conclusión

En resumen, la herencia se centra en la relación "es un" y se utiliza para modelar jerarquías de clases, mientras que la composición se centra en la relación "tiene un" y se utiliza para construir objetos complejos a partir de otros objetos más simples. Ambos enfoques tienen sus casos de uso específicos y se eligen según la estructura y la naturaleza de las relaciones en el diseño del software.

以上が構成と継承の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。