Diferencia entre herencia y composición

208

¿Son iguales la composición y la herencia? Si quiero implementar el patrón de composición, ¿cómo puedo hacer eso en Java?

gmhk
fuente
2
Otra pregunta relacionada: ¿Hay algo que la composición no pueda lograr que la herencia pueda? stackoverflow.com/questions/2238642/…
ewernli
1
También vea is-a-vs-has-a-which-one-is-better
nawfal el
1
Este artículo fue útil para mí y así ayudó: thoughtworks.com/insights/blog/...
qmaster
echa un vistazo a themightyprogrammer.dev/article/inheritance-composition
The Mighty Programmer

Respuestas:

321

Son absolutamente diferentes La herencia es una relación "es-a" . La composición es un "has-a" .

Realiza la composición teniendo una instancia de otra clase Ccomo un campo de su clase, en lugar de extender C. Un buen ejemplo donde la composición hubiera sido mucho mejor que la herencia es java.util.Stack, que actualmente se extiende java.util.Vector. Esto ahora se considera un error. Un vector de pila "no es un" ; no se le debe permitir insertar y eliminar elementos arbitrariamente. Debería haber sido composición en su lugar.

Desafortunadamente, es demasiado tarde para rectificar este error de diseño, ya que cambiar la jerarquía de herencia ahora rompería la compatibilidad con el código existente. Si hubiera Stackutilizado la composición en lugar de la herencia, siempre se puede modificar para usar otra estructura de datos sin violar la API .

Recomiendo el libro de Josh Bloch, Effective Java 2nd Edition

  • Tema 16: favorecer la composición sobre la herencia
  • Punto 17: Diseñe y documente la herencia o, de lo contrario, prohíba

Un buen diseño orientado a objetos no se trata de ampliar liberalmente las clases existentes. Su primer instinto debería ser componer en su lugar.


Ver también:

poligenelubricantes
fuente
44
Interesante. ¿Por qué no simplemente crear una nueva clase java.util.Stack2 que usa composición?
qed
55
Agradezco esta respuesta; Sin embargo, siento que la respuesta se desvía y profundiza más en las preocupaciones que rodean el diseño del lenguaje (y un paquete en particular) que en la pregunta formulada sobre composición frente a herencia. Soy un gran admirador de responder la pregunta sobre SO, citando recursos, no vincular a recursos externos sin proporcionar un resumen más profundo que un resumen de una línea.
Thomas
55
Mal ejemplo, tuve que hacer una búsqueda adicional para comprender qué son Vector y Stack.
Sam Ramezanli
Bueno, pero tu ejemplo sobre la pila de Java no es un buen candidato porque de esta herencia es una muestra de mala decisión sobre la elección de la herencia sobre la composición como se ha dicho en este artículo: thoughtworks.com/insights/blog/...
qmaster
212

Composición significa HAS A
herencia significaIS A

Example: El automóvil tiene un motor y el automóvil es un automóvil

En programación esto se representa como:

class Engine {} // The Engine class.

class Automobile {} // Automobile class which is parent to Car class.

class Car extends Automobile { // Car is an Automobile, so Car class extends Automobile class.
  private Engine engine; // Car has an Engine so, Car class has an instance of Engine class as its member.
}
codictorio
fuente
77
Cambiaría "automóvil" por "vehículo motorizado", ya que en muchas interpretaciones los automóviles y automóviles son equivalentes.
nanofarad
55
@hexafraction Estoy de acuerdo en que Vehículo probablemente sería una mejor opción, pero al mismo tiempo -codaddict- ha ilustrado bien el punto de lo que se ha pedido.
nckbrz
55
"un motor":-/
Omar Tariq
bien expandido +1. lecturas adicionales javarevisited.blogspot.in/2015/06/…
roottraveller
@AndreyAkhmetov Automobile puede tener un typecampo de TipoEnum
Ojonugwa Jude Ochalifu
42

¿Cómo la herencia puede ser peligrosa?

Tomemos un ejemplo

public class X{    
   public void do(){    
   }    
}    
Public Class Y extends X{
   public void work(){    
       do();    
   }
}

1) Como está claro en el código anterior, la Clase Y tiene un acoplamiento muy fuerte con la clase X. Si algo cambia en la superclase X, Y puede romperse dramáticamente. Supongamos que en el futuro la clase X implementa un método de trabajo con la firma a continuación

public int work(){
}

El cambio se realiza en la clase X pero hará que la clase Y no sea compatible. Entonces, este tipo de dependencia puede subir a cualquier nivel y puede ser muy peligroso. Cada vez que la superclase puede no tener visibilidad completa para codificar dentro de todas sus subclases y la subclase puede seguir notando todo el tiempo lo que sucede en la superclase. Por lo tanto, debemos evitar este acoplamiento fuerte e innecesario.

¿Cómo resuelve la composición este problema?

Veamos revisando el mismo ejemplo

public class X{
    public void do(){
    }
}

Public Class Y{
    X x = new X();    
    public void work(){    
        x.do();
    }
}

Aquí estamos creando referencia de clase X en clase Y e invocando método de clase X creando una instancia de clase X. Ahora todo ese fuerte acoplamiento se ha ido. Superclase y subclase son altamente independientes entre sí ahora. Las clases pueden realizar libremente cambios que eran peligrosos en la situación de herencia.

2) Segunda ventaja muy buena de la composición, ya que proporciona flexibilidad para llamar a métodos, por ejemplo:

class X implements R
{}
class Y implements R
{}

public class Test{    
    R r;    
}

En la clase de prueba usando la referencia r, puedo invocar métodos de la clase X y de la clase Y. Esta flexibilidad nunca estuvo allí en la herencia

3) Otra gran ventaja: pruebas unitarias

public class X {
    public void do(){
    }
}

Public Class Y {
    X x = new X();    
    public void work(){    
        x.do();    
    }    
}

En el ejemplo anterior, si no se conoce el estado de la instancia x, se puede burlar fácilmente utilizando algunos datos de prueba y todos los métodos se pueden probar fácilmente. Esto no era posible en absoluto en la herencia, ya que dependía en gran medida de la superclase para obtener el estado de instancia y ejecutar cualquier método.

4) Otra buena razón por la que debemos evitar la herencia es que Java no admite la herencia múltiple.

Tomemos un ejemplo para entender esto:

Public class Transaction {
    Banking b;
    public static void main(String a[])    
    {    
        b = new Deposit();    
        if(b.deposit()){    
            b = new Credit();
            c.credit();    
        }
    }
}

Bueno saber :

  1. la composición se logra fácilmente en tiempo de ejecución, mientras que la herencia proporciona sus características en tiempo de compilación

  2. la composición también se conoce como relación HAS-A y la herencia también se conoce como relación IS-A

Por lo tanto, habitúese a preferir siempre la composición sobre la herencia por varias razones anteriores.

Manoj Kumar Saini
fuente
3
de acuerdo, pero dada su solución usando composición ... todavía necesitamos hacer un canguro, por ejemplo, ahora la superclase X cambia el nombre del método de hacer a hacer ... luego la subclase Y también deberá mantenerse (debe cambiarse también) ¿esto sigue siendo un acoplamiento apretado? ¿Y cómo nos deshacemos de él?
stuckedoverflow
19

La respuesta dada por @Michael Rodrigues no es correcta (pido disculpas; no puedo comentar directamente), y podría generar cierta confusión.

La implementación de la interfaz es una forma de herencia ... cuando implementa una interfaz, no solo hereda todas las constantes, sino que confirma que su objeto es del tipo especificado por la interfaz; sigue siendo una relación " es-a ". Si un automóvil implementa Rellenable , el automóvil " es un " Rellenable , y puede usarse en su código donde sea que use un Rellenable .

La composición es fundamentalmente diferente de la herencia. Cuando usa la composición, está (como señalan las otras respuestas) haciendo una relación " tiene-a " entre dos objetos, a diferencia de la relación " es-a " que establece cuando usa la herencia .

Entonces, de los ejemplos de autos en las otras preguntas, si quisiera decir que un auto tiene un tanque de gasolina, usaría la composición de la siguiente manera:

public class Car {

private GasTank myCarsGasTank;

}

Esperemos que eso aclare cualquier malentendido.

Kris
fuente
17

La herencia pone de manifiesto la relación IS-A . La composición resalta la relación HAS-A . El patrón de estrategia explica que la Composición debe usarse en casos en los que hay familias de algoritmos que definen un comportamiento particular.
Ejemplo clásico de una clase de pato que implementa un comportamiento de vuelo.

public interface Flyable{
 public void fly();
}

public class Duck {
 Flyable fly;

 public Duck(){
  fly = new BackwardFlying();
 }
}

Por lo tanto, podemos tener múltiples clases que implementan volar, por ejemplo:

public class BackwardFlying implements Flyable{
  public void fly(){
    Systemout.println("Flies backward ");
  }
}
public class FastFlying implements Flyable{
  public void fly(){
    Systemout.println("Flies 100 miles/sec");
  }
}

Si hubiera sido por herencia, tendríamos dos clases diferentes de aves que implementan la función de volar una y otra vez. Entonces la herencia y la composición son completamente diferentes.

polea sin fricción
fuente
7

La composición es tal como suena: crea un objeto conectando partes.

EDITAR el resto de esta respuesta se basa erróneamente en la siguiente premisa.
Esto se logra con las interfaces.
Por ejemplo, usando el Carejemplo anterior,

Car implements iDrivable, iUsesFuel, iProtectsOccupants
Motorbike implements iDrivable, iUsesFuel, iShortcutThroughTraffic
House implements iProtectsOccupants
Generator implements iUsesFuel

Entonces, con algunos componentes teóricos estándar, puede construir su objeto. Entonces es su trabajo completar cómo Houseprotege a sus ocupantes y cómo Carprotege a sus ocupantes.

La herencia es como al revés. Comienza con un objeto completo (o semi-completo) y reemplaza o anula los diversos bits que desea cambiar.

Por ejemplo, MotorVehiclepuede venir con un Fuelablemétodo y Drivemétodo. Puede dejar el método Fuel como está porque es lo mismo para llenar una moto y un automóvil, pero puede anular el Drivemétodo porque la Moto conduce de manera muy diferente a a Car.

Con la herencia, algunas clases ya están completamente implementadas, y otras tienen métodos que se ven obligados a anular. Con Composition no se te da nada. (pero puede implementar las interfaces llamando a métodos en otras clases si tiene algo por ahí).

La composición se considera más flexible, porque si tiene un método como iUsesFuel, puede tener un método en otro lugar (otra clase, otro proyecto) que solo se preocupa por tratar con objetos que pueden ser alimentados, independientemente de si es un automóvil, barco, estufa, barbacoa, etc. Las interfaces exigen que las clases que dicen que implementan esa interfaz realmente tengan los métodos de los que se trata esa interfaz. Por ejemplo,

iFuelable Interface:
   void AddSomeFuel()
   void UseSomeFuel()
   int  percentageFull()

entonces puedes tener un método en otro lugar

private void FillHerUp(iFuelable : objectToFill) {

   Do while (objectToFill.percentageFull() <= 100)  {

        objectToFill.AddSomeFuel();
   }

Extraño ejemplo, pero muestra que a este método no le importa lo que se está llenando, porque el objeto se implementa iUsesFuel, se puede llenar. Fin de la historia.

Si usaste Herencia en su lugar, necesitarías diferentes FillHerUpmétodos para tratar MotorVehiclesy Barbecues, a menos que tuvieras algún objeto base "ObjectThatUsesFuel" bastante extraño del cual heredar.

Michael Rodrigues
fuente
Las convenciones de Java establecen que los nombres de clase e interfaz están escritos ThisCase, no en camelCase. Por lo tanto, es mejor nombrar sus interfaces IDrivable, etc. Es posible que no necesite la "I" si reagrupa todas sus interfaces en un paquete correctamente.
ThePyroEagle
6

¿Son iguales la composición y la herencia?

No son lo mismo.

Composición : Permite que un grupo de objetos tenga que ser tratado de la misma manera que una sola instancia de un objeto. La intención de un compuesto es "componer" objetos en estructuras de árbol para representar jerarquías de parte completa.

Herencia : una clase hereda campos y métodos de todas sus superclases, ya sean directas o indirectas. Una subclase puede anular los métodos que hereda o puede ocultar los campos o métodos que hereda.

Si quiero implementar el patrón de composición, ¿cómo puedo hacer eso en Java?

El artículo de Wikipedia es lo suficientemente bueno como para implementar un patrón compuesto en Java.

ingrese la descripción de la imagen aquí

Participantes clave:

Componente :

  1. Es la abstracción para todos los componentes, incluidos los compuestos.
  2. Declara la interfaz para los objetos en la composición.

Hoja :

  1. Representa objetos de hoja en la composición.
  2. Implementa todos los métodos de componentes.

Compuesto :

  1. Representa un componente compuesto (componente que tiene hijos)
  2. Implementa métodos para manipular a los niños.
  3. Implementa todos los métodos de componentes, generalmente delegándolos a sus hijos

Ejemplo de código para comprender el patrón compuesto :

import java.util.List;
import java.util.ArrayList;

interface Part{
    public double getPrice();
    public String getName();
}
class Engine implements Part{
    String name;
    double price;
    public Engine(String name,double price){
        this.name = name;
        this.price = price;
    }
    public double getPrice(){
        return price;
    }
    public String getName(){
        return name;
    }
}
class Trunk implements Part{
    String name;
    double price;
    public Trunk(String name,double price){
        this.name = name;
        this.price = price;
    }
    public double getPrice(){
        return price;
    }
    public String getName(){
        return name;
    }
}
class Body implements Part{
    String name;
    double price;
    public Body(String name,double price){
        this.name = name;
        this.price = price;
    }
    public double getPrice(){
        return price;
    }
    public String getName(){
        return name;
    }
}
class Car implements Part{
    List<Part> parts;
    String name;

    public Car(String name){
        this.name = name;
        parts = new ArrayList<Part>();
    }
    public void addPart(Part part){
        parts.add(part);
    }
    public String getName(){
        return name;
    }
    public String getPartNames(){
        StringBuilder sb = new StringBuilder();
        for ( Part part: parts){
            sb.append(part.getName()).append(" ");
        }
        return sb.toString();
    }
    public double getPrice(){
        double price = 0;
        for ( Part part: parts){
            price += part.getPrice();
        }
        return price;
    }   
}

public class CompositeDemo{
    public static void main(String args[]){
        Part engine = new Engine("DiselEngine",15000);
        Part trunk = new Trunk("Trunk",10000);
        Part body = new Body("Body",12000);

        Car car = new Car("Innova");
        car.addPart(engine);
        car.addPart(trunk);
        car.addPart(body);

        double price = car.getPrice();

        System.out.println("Car name:"+car.getName());
        System.out.println("Car parts:"+car.getPartNames());
        System.out.println("Car price:"+car.getPrice());
    }

}

salida:

Car name:Innova
Car parts:DiselEngine Trunk Body
Car price:37000.0

Explicación:

  1. Parte es una hoja
  2. El auto contiene muchas partes
  3. Se han agregado diferentes partes del automóvil al automóvil
  4. El precio del automóvil = suma de (precio de cada parte )

Consulte la siguiente pregunta para Pros y Contras de Composición y Herencia.

¿Prefieres la composición a la herencia?

Ravindra babu
fuente
¿Tiene sentido implementar piezas para la clase de automóviles también? ¿No será bueno si usted lo usa como composición
bhalkian
4

Como otro ejemplo, considere una clase de automóvil, este sería un buen uso de la composición, un automóvil "tendría" un motor, una transmisión, llantas, asientos, etc. No ampliaría ninguna de esas clases.

BlackICE
fuente
4

La composición es donde algo se compone de partes distintas y tiene una fuerte relación con esas partes. Si la parte principal muere, también lo hacen los demás, no pueden tener vida propia. Un ejemplo aproximado es el cuerpo humano. Saca el corazón y todas las otras partes mueren.

La herencia es donde simplemente tomas algo que ya existe y lo usas. No hay una relación fuerte. Una persona podría heredar la herencia de su padre, pero puede prescindir de ella.

No conozco Java, así que no puedo proporcionar un ejemplo, pero sí puedo proporcionar una explicación de los conceptos.

Robert Rocha
fuente
3

La herencia entre dos clases, donde una clase se extiende a otra, establece la relación " IS A ".

La composición en el otro extremo contiene una instancia de otra clase en su clase que establece la relación " Tiene A ". La composición en Java es útil ya que técnicamente facilita la herencia múltiple.

John Wilson
fuente
3

En palabras simples, la agregación significa tiene una relación.

La composición es un caso especial de agregación . De una manera más específica, una agregación restringida se llama composición. Cuando un objeto contiene el otro objeto, si el objeto contenido no puede existir sin la existencia del objeto contenedor, entonces se llama composición. Ejemplo: una clase contiene estudiantes. Un estudiante no puede existir sin una clase. Existe composición entre clase y alumnos.

Por qué usar la agregación

Código de reutilización

Cuando usar agregación

La reutilización del código también se logra mejor mediante la agregación cuando no hay un barco de Relación

Herencia

La herencia es una relación padre-hijo La herencia significa es una relación

La herencia en Java es un mecanismo en el cual un objeto adquiere todas las propiedades y comportamientos del objeto padre.

Uso de herencia en Java 1 Code Reusability. 2 Agregue una característica adicional en la clase secundaria, así como la anulación del método (para que se pueda lograr el polimorfismo en tiempo de ejecución).

Keshav Gera
fuente
1

Aunque tanto la herencia como la composición proporcionan la reutilización del código, la principal diferencia entre la composición y la herencia en Java es que la composición permite la reutilización del código sin extenderlo, pero para la herencia debe extender la clase para cualquier reutilización de código o funcionalidad. Otra diferencia que surge de este hecho es que al usar Composición puede reutilizar el código incluso para la clase final que no es extensible, pero la Herencia no puede reutilizar el código en tales casos. Además, al usar Composición, puede reutilizar el código de muchas clases, ya que se declaran solo como una variable miembro, pero con Herencia puede reutilizar el código de una sola clase porque en Java solo puede extender una clase, porque la herencia múltiple no es compatible con Java . Sin embargo, puede hacer esto en C ++ porque allí una clase puede extender más de una clase. Por cierto, siempre debesPrefiero la composición a la herencia en Java , no solo soy yo, sino que incluso Joshua Bloch ha sugerido en su libro

Nitin Pawar
fuente
1
¿Por qué debería "preferir Composición sobre Herencia "? Son conceptos diferentes y se usan para diferentes propósitos. Francamente, no veo cómo podrías pasar de uno a otro.
Kröw
1

Creo que este ejemplo explica claramente las diferencias entre herencia y composición .

En este ejemplo, el problema se resuelve usando herencia y composición. El autor presta atención al hecho de que; en herencia , un cambio en la superclase podría causar problemas en la clase derivada, que la hereda.

Allí también puede ver la diferencia en la representación cuando usa un UML para herencia o composición.

http://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--which-one-should-you-choose-.html

aunte
fuente
1
Las respuestas de solo enlace se desaconsejan porque quedan obsoletas. Incluya al menos la información relevante más importante del enlace en su respuesta.
Scott Solmer
1

Herencias Vs Composición.

Las herencias y la composición se utilizan tanto para la reutilización como para la extensión del comportamiento de la clase.

Las herencias se usan principalmente en un modelo de programación de algoritmos familiares, como el tipo de relación IS-A, que significa un tipo de objeto similar. Ejemplo.

  1. Duster es un auto
  2. Safari es un auto

Estos pertenecen a la familia Car.

La composición representa el tipo de relación HAS-A. Muestra la capacidad de un objeto como Duster tiene cinco engranajes, Safari tiene cuatro engranajes, etc. Siempre que necesitemos ampliar la capacidad de una clase existente, utilice la composición. Ejemplo: necesitamos agregar un engranaje más en el objeto Duster, luego tenemos que crear un objeto de engranaje más y componerlo en el objeto Duster.

No deberíamos hacer los cambios en la clase base hasta / a menos que todas las clases derivadas necesiten esa funcionalidad. Para este escenario deberíamos usar Composición.

clase A derivada por clase B

Clase A Derivada por Clase C

Clase A Derivada por Clase D.

Cuando agregamos cualquier funcionalidad en la clase A, entonces está disponible para todas las subclases, incluso cuando la Clase C y D no requieren esa funcionalidad. Para este escenario, necesitamos crear una clase separada para esa funcionalidad y componerla en la clase requerida ( Aquí está la clase B).

A continuación se muestra el ejemplo:

          // This is a base class
                 public abstract class Car
                    {
                       //Define prototype
                       public abstract void color();
                       public void Gear() {
                           Console.WriteLine("Car has a four Gear");
                       }
                    }


           // Here is the use of inheritence
           // This Desire class have four gears.
          //  But we need to add one more gear that is Neutral gear.

          public class Desire : Car
                   {
                       Neutral obj = null;
                       public Desire()
                       {
     // Here we are incorporating neutral gear(It is the use of composition). 
     // Now this class would have five gear. 

                           obj = new Neutral();
                           obj.NeutralGear();
                       }

                       public override void color()
                       {
                           Console.WriteLine("This is a white color car");
                       }

                   }


             // This Safari class have four gears and it is not required the neutral
             //  gear and hence we don't need to compose here.

                   public class Safari :Car{
                       public Safari()
                       { }

                       public override void color()
                       {
                           Console.WriteLine("This is a red color car");
                       }


                   }

   // This class represents the neutral gear and it would be used as a composition.

   public class Neutral {
                      public void NeutralGear() {
                           Console.WriteLine("This is a Neutral Gear");
                       }
                   }
Sheo Dayal Singh
fuente
0

Composición significa crear un objeto para una clase que tenga relación con esa clase en particular. Supongamos que el estudiante tiene relación con las cuentas;

Una herencia es que esta es la clase anterior con la característica extendida. Eso significa que esta nueva clase es la clase Old con alguna característica extendida. Supongamos que el estudiante es estudiante pero todos los estudiantes son humanos. Entonces hay una relación con el estudiante y el humano. Esto es herencia.

HM Nayem
fuente
0

No, ambos son diferentes. La composición sigue la relación "HAS-A" y la herencia sigue la relación "IS-A". El mejor ejemplo para la composición fue el patrón estratégico.

Ranga Reddy
fuente
2
Su única calidad de comentario
Mathews Sunny
¿Acabas de comentar lo mismo que la respuesta aceptada dijo 7 años antes que tú?
Anjil Dhamala
0

Herencia significa reutilizar la funcionalidad completa de una clase. Aquí mi clase tiene que usar todos los métodos de la superclase y mi clase se unirá a la superclase y el código se duplicará en ambas clases en caso de herencia.

Pero podemos superar todos estos problemas cuando usamos la composición para hablar con otra clase. composición está declarando un atributo de otra clase en mi clase con la que queremos hablar. y qué funcionalidad queremos de esa clase podemos obtener usando ese atributo.

Vikas Kapadiya
fuente