Codigo limpio y refactorización
Usa composición (delegación de comportamientos) en lugar de herencia para evitar acoplamiento rígido.
Composición y herencia son dos conceptos clave en la programación orientada a objetos para estructurar y organizar el código. Aunque la herencia tiene su lugar en ciertos escenarios, el principio de "preferir composición sobre herencia" (composition over inheritance) ayuda a crear sistemas más flexibles, reutilizables y desacoplados.
Ventajas de Usar Composición
-
Evita Jerarquías Rígidas: No dependes de una estructura de herencia que puede volverse difícil de mantener.
-
Reutilización: Los comportamientos (
PuedeVolar,PuedeNadar) pueden ser usados por múltiples clases. -
Mayor Flexibilidad: Puedes cambiar o extender los comportamientos sin afectar a otras partes del sistema.
-
Mantenibilidad: Las clases tienen responsabilidades claras y están desacopladas.
Cuándo Usar Composición en Lugar de Herencia
-
Cuando una relación "tiene un" es más adecuada que "es un". Ejemplo: Un auto "tiene un" motor, no "es un" motor.
-
Cuando quieres evitar métodos irrelevantes en las subclases. Ejemplo: Un pingüino no necesita un método
volar. -
Cuando necesitas reutilización sin rigidez. Ejemplo: Comportamientos reutilizables como
PuedeNadaroPuedeVolar. -
En sistemas que requieren flexibilidad y modularidad. Al usar composición, obtienes un diseño más modular, flexible y fácil de mantener, eliminando las limitaciones de las jerarquías de herencia rígidas. 😊
Diferencias Clave
| Aspecto | Herencia | Composición |
|---|---|---|
| Relación | "Es un" (is-a). Una clase hereda de otra. | "Tiene un" (has-a). Una clase contiene instancias de otras clases. |
| Acoplamiento | Acopla fuertemente la clase hija a la clase base. | Desacopla las clases, ya que los comportamientos se delegan a componentes. |
| Reutilización | Los métodos de la clase base se reutilizan, pero heredan toda su implementación. | Permite reutilizar comportamientos específicos sin heredar implementaciones innecesarias. |
| Escalabilidad | Difícil de cambiar si la jerarquía de clases crece mucho. | Fácil de extender y modificar agregando o reemplazando componentes. |
| Flexibilidad | Poco flexible, ya que las clases hijas dependen de la estructura base. | Muy flexible, ya que los componentes pueden combinarse de muchas formas. |
Código Malo
1class Motor { 2 public void arrancar() { 3 System.out.println("Motor arrancado"); 4 } 5} 6 7// Subclase para coche eléctrico 8class CocheElectrico extends Motor { 9 @Override 10 public void arrancar() { 11 System.out.println("Motor eléctrico en marcha silenciosa"); 12 } 13 14 public void cargarBateria() { 15 System.out.println("Cargando batería..."); 16 } 17} 18 19// Subclase para coche de combustión 20class CocheCombustion extends Motor { 21 @Override 22 public void arrancar() { 23 System.out.println("Motor de combustión encendido"); 24 } 25 26 public void llenarDeposito() { 27 System.out.println("Llenando el depósito de combustible..."); 28 } 29}
Problema: Heredar
Motorpara unCochees incorrecto.
Solución: Usa composición para manejar comportamientos específicos en lugar de herencia.
Código Bueno
1interface Motor { 2 void arrancar(); 3} 4 5class MotorElectrico implements Motor { 6 @Override 7 public void arrancar() { 8 System.out.println("Motor eléctrico en marcha silenciosa"); 9 } 10 11 public void cargarBateria() { 12 System.out.println("Cargando batería..."); 13 } 14} 15 16class MotorCombustion implements Motor { 17 @Override 18 public void arrancar() { 19 System.out.println("Motor de combustión encendido"); 20 } 21 22 public void llenarDeposito() { 23 System.out.println("Llenando el depósito de combustible..."); 24 } 25} 26 27class Coche { 28 private Motor motor; 29 30 public Coche(Motor motor) { 31 this.motor = motor; 32 } 33 34 public void arrancarMotor() { 35 motor.arrancar(); 36 } 37}
- Loading...