Maison  >  Article  >  Java  >  Composition vs héritage

Composition vs héritage

DDD
DDDoriginal
2024-09-25 11:45:44457parcourir

Composición vs Herencia

Introduction

L'héritage et la composition sont deux concepts fondamentaux de la programmation orientée objet (POO), mais ils sont utilisés différemment et ont des objectifs différents. L'objectif de cet article est de passer en revue ces objectifs et certains éléments à garder à l'esprit lors de leur choix.

Notions d'héritage

Lorsque nous pensons à appliquer le patrimoine dans nos créations, nous devons comprendre :

  • Définition : En héritage, une classe (appelée classe ou sous-classe dérivée) peut hériter des propriétés et des comportements d'une autre classe (appelée classe de base ou superclasse). La classe dérivée peut étendre ou modifier les fonctionnalités de la classe de base.
  • Relation : C'est une relation "est un" (est-un). Par exemple, si vous avez une classe « Véhicule » et une autre classe « Voiture », la classe « Voiture » ​​est une sous-classe de « Véhicule ».
  • Avantages : Favorise la réutilisation du code et permet une extension facile des fonctionnalités.

Notions de composition

Par contre, si l'on pense composer des objets entre eux :

  • Définition : En composition, un objet contient d'autres objets et leur délègue une partie de ses fonctionnalités. Au lieu de l'héritage, une classe utilise des instances d'autres classes pour atteindre ses fonctionnalités.
  • Relation : C'est une relation "has-a" (has-a). Par exemple, si vous avez une classe "Engine" et une classe "Car", la classe "Car" peut avoir un objet de classe "Engine".
  • Avantages : Plus de flexibilité et moins de couplage entre les cours. Les changements dans une classe n'affectent pas directement l'autre.

Pourquoi un concordat sur l'héritage ?

La question de savoir si la composition est meilleure que l'héritage ou vice versa est un sujet débattu dans la conception de logiciels. Les deux approches ont leurs avantages et leurs inconvénients, et le choix dépend du contexte et des exigences spécifiques du projet. Je vais vous présenter ici un exemple où la composition peut être préférable à l'héritage.

Explorons un exemple en Java qui illustre comment la composition peut être préférable à l'héritage dans certains cas. Supposons que nous travaillions sur un système de traitement des commandes dans une boutique en ligne.

  • Approche successorale :

Tout d'abord, considérons une approche utilisant l'héritage pour représenter différents types de produits pouvant être achetés, tels que les livres et les appareils électroniques :

// 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;
    }
}

Cette approche fonctionne, mais que se passe-t-il si vous devez introduire de nouveaux types de produits ou ajouter des fonctionnalités spécifiques pour certains types de produits ?

  • Concentrez-vous avec la composition :

Au lieu de compter entièrement sur l'héritage, nous pourrions utiliser la composition pour gérer différents types de produits de manière plus flexible :

// 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);
    }
}

Dans cette approche, chaque type de produit possède une instance de la classe Product, permettant de partager une logique commune pour le traitement des commandes. De plus, chaque type de produit peut avoir sa propre logique spécifique à l'aide de méthodes telles que processSpecificOrder(). Cette conception est plus flexible et facilite l'introduction de nouveaux types de produits ou la modification de la logique spécifique au type sans affecter la hiérarchie d'héritage.

Quand appliquer l’héritage ?

Alors que le choix entre l'héritage et la composition dans la conception de logiciels dépend du contexte et des exigences spécifiques du problème que vous abordez. Voici quelques situations dans lesquelles vous pourriez considérer l’héritage comme une option plus appropriée que la composition :

  • 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.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Article précédent:Chapitre 7 Tests finauxArticle suivant:Chapitre 7 Tests finaux