Composición vs Herencia

Composición vs Herencia es una de las preguntas frecuentes en entrevistas. Seguro también has escuchado que se debe utilizar la Composición en lugar de la Herencia.

Composición vs Herencia

Ambos, la composición y la herencia, son conceptos de programación orientada a objetos. No están vinculados a un lenguaje de programación específico como Java. Antes de comparar la composición sobre la herencia programáticamente, veamos una definición rápida de ambas.

Composición

La composición es una técnica de diseño en programación orientada a objetos para implementar una relación de “tiene-un” entre objetos. En Java, la composición se logra mediante el uso de variables de instancia de otros objetos. Por ejemplo, una persona que tiene un trabajo se implementa de la siguiente manera en la programación orientada a objetos de Java.

package com.journaldev.composition;

public class Job {
// variables, métodos, etc.
}
package com.journaldev.composition;

public class Person {

    // relación de composición "tiene-un"
    private Job job;

    // variables, métodos, constructores, etc. orientado a objetos

Herencia

La herencia es la técnica de diseño en la programación orientada a objetos para implementar la relación es-un entre objetos. La herencia en Java se implementa utilizando la palabra clave extends. Por ejemplo, la relación de “Gato es un Animal” en la programación Java se implementará de la siguiente manera.

package com.journaldev.inheritance;
 
public class Animal {
// variables, métodos, etc.
}
package com.journaldev.inheritance;
 
public class Cat extends Animal{
}

Composición sobre Herencia

Tanto la composición como la herencia promueven la reutilización de código a través de enfoques diferentes. Entonces, ¿cuál elegir? ¿Cómo comparar composición vs. herencia? Seguramente habrás escuchado que en la programación se debe favorecer la composición sobre la herencia. Veamos algunas razones que te ayudarán a elegir entre composición y herencia.

  1. La herencia está estrechamente acoplada, mientras que la composición está débilmente acoplada. Supongamos que tenemos las siguientes clases con herencia.

    package com.journaldev.java.examples;
    
    public class ClassA {
    
    	public void foo(){	
    	}
    }
    
    class ClassB extends ClassA{
    	public void bar(){
    		
    	}
    }
    

    Para simplificar, tenemos tanto la superclase como la subclase en un solo paquete. Pero mayormente estarán en bases de código separadas. Podría haber muchas clases extendiendo la superclase ClassA. Un ejemplo muy común de esta situación es extender la clase Exception. Ahora digamos que la implementación de ClassA cambia como a continuación, se agrega un nuevo método bar().

    package com.journaldev.java.examples;
    
    public class ClassA {
    
    	public void foo(){	
    	}
    	
    	public int bar(){
    		return 0;
    	}
    }
    

    Tan pronto como comiences a usar la nueva implementación de ClassA, obtendrás un error de tiempo de compilación en ClassB ya que El tipo de retorno es incompatible con ClassA.bar(). La solución sería cambiar ya sea el método bar() de la superclase o de la subclase para hacerlos compatibles. Si hubieras usado Composición en lugar de herencia, nunca enfrentarías este problema. Un ejemplo simple de la implementación de ClassB usando Composición puede ser como a continuación.

    class ClassB{
    	ClassA classA = new ClassA();
    	
    	public void bar(){
    		classA.foo();
    		classA.bar();
    	}
    }
    
  2. No hay control de acceso en la herencia, mientras que el acceso puede restringirse en la composición. Exponemos todos los métodos de la superclase a las demás clases que tienen acceso a la subclase. Entonces, si se introduce un nuevo método o hay agujeros de seguridad en la superclase, la subclase se vuelve vulnerable. Dado que en la composición elegimos qué métodos usar, es más seguro que la herencia. Por ejemplo, podemos proporcionar la exposición del método foo() de ClassA a otras clases utilizando el siguiente código en ClassB.

    class ClassB {
    	
    	ClassA classA = new ClassA();
    	
    	public void foo(){
    		classA.foo();
    	}
    	
    	public void bar(){	
    	}
    	
    }
    

    Esta es una de las principales ventajas de la composición sobre la herencia.

  3. La composición proporciona flexibilidad en la invocación de métodos que es útil con múltiples escenarios de subclases. Por ejemplo, supongamos que tenemos el siguiente escenario de herencia.

    abstract class Abs {
    	abstract void foo();
    }
    
    public class ClaseA extends Abs{
    
    	public void foo(){	
    	}
    	
    }
    
    class ClaseB extends Abs{
    		
    	public void foo(){
    	}
    	
    }
    
    class Test {
    	
    	ClaseA a = new ClaseA();
    	ClaseB b = new ClaseB();
    
    	public void test(){
    		a.foo();
    		b.foo();
    	}
    }
    

    Entonces, ¿qué pasa si hay más subclases? ¿Hará la composición que nuestro código sea feo al tener una instancia para cada subclase? No, podemos reescribir la clase Test de la siguiente manera.

    class Test {
    	Abs obj = null;
    	
    	Test1(Abs o){
    		this.obj = o;
    	}
    	
    	public void foo(){
    		this.obj.foo();
    	}
    
    }
    

    Esto le dará la flexibilidad de utilizar cualquier subclase según el objeto utilizado en el constructor.

  4. Un beneficio adicional de la composición sobre la herencia es el alcance de las pruebas. Las pruebas unitarias son fáciles con la composición porque sabemos qué métodos estamos utilizando de otra clase. Podemos simularlo para las pruebas, mientras que en la herencia dependemos en gran medida de la superclase y no sabemos qué métodos de la superclase se utilizarán. Entonces, tendremos que probar todos los métodos de la superclase. Esto es trabajo adicional y lo hacemos innecesariamente debido a la herencia.

Eso es todo sobre composición versus herencia. Tienes razones suficientes para elegir la composición sobre la herencia. Utiliza la herencia solo cuando estés seguro de que la superclase no cambiará; de lo contrario, opta por la composición.

Source:
https://www.digitalocean.com/community/tutorials/composition-vs-inheritance