Inheritance and composition are two fundamental concepts in object-oriented programming (OOP), but they are used differently and have different purposes. The objective of this post is to review those purposes, and some things to keep in mind when choosing them.
When we think about applying heritage in our designs, we have to understand:
On the other hand, if we think about composing objects with each other:
The question of whether composition is better than inheritance or vice versa is a debated topic in software design. Both approaches have their advantages and disadvantages, and the choice depends on the specific project context and requirements. Here I will present you an example where composition can be preferable to inheritance.
Let's explore an example in Java that illustrates how composition can be preferable to inheritance in certain cases. Suppose we are working on an order processing system in an online store.
First, let's consider an approach using inheritance to represent different types of products that can be purchased, such as books and electronics:
// 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; } }
This approach works, but what if you need to introduce new product types or add specific functionality for certain product types?
Instead of relying entirely on inheritance, we could use composition to handle different types of products more flexibly:
// 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); } }
In this approach, each product type has an instance of the Product class, allowing common logic to be shared for processing orders. Additionally, each product type can have its own specific logic using methods such as processSpecificOrder(). This design is more flexible and makes it easier to introduce new product types or modify type-specific logic without affecting the inheritance hierarchy.
While the choice between inheritance and composition in software design depends on the context and specific requirements of the problem you are addressing. Here are some situations where you might consider inheritance as a more appropriate option than composition:
class Vehiculo { // ... } class Automovil extends Vehiculo { // ... }
class Animal { void comer() { // Lógica común para comer } } class Perro extends Animal { void ladrar() { // Lógica específica para ladrar } }
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 } }
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.
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.
The above is the detailed content of Composition vs Inheritance. For more information, please follow other related articles on the PHP Chinese website!