Diferença entre herança e composição


208

Composição e herança são iguais? Se eu quiser implementar o padrão de composição, como posso fazer isso em Java?


2
Outra questão relacionada: existe algo que a composição não possa realizar que a herança possa? stackoverflow.com/questions/2238642/…
ewernli


1
Este artigo foi útil para mim e por isso ajudou: thoughtworks.com/insights/blog/...
Qmaster

Respostas:


321

Eles são absolutamente diferentes. A herança é um relacionamento "é-a" . A composição é um "tem-a" .

Você faz composição tendo uma instância de outra classe Ccomo um campo da sua classe, em vez de se estendendo C. Um bom exemplo de composição seria muito melhor do que a herança java.util.Stack, que atualmente se estende java.util.Vector. Agora isso é considerado um erro. Um vetor "is-NOT-a" da pilha ; você não deve inserir e remover elementos arbitrariamente. Deveria ter sido composição em seu lugar.

Infelizmente, é tarde demais para corrigir esse erro de design, já que alterar a hierarquia de herança agora quebraria a compatibilidade com o código existente. Se havia Stackusado composição em vez de herança, sempre pode ser modificado para usar outra estrutura de dados sem violar a API .

Eu recomendo o livro de Josh Bloch, Effective Java 2nd Edition

  • Item 16: Favorecer a composição sobre a herança
  • Item 17: Design e documento da herança ou então proibi-la

Um bom design orientado a objetos não se estende liberalmente às classes existentes. Seu primeiro instinto deve ser compor.


Veja também:


4
Interessante. Por que não criar apenas uma nova classe java.util.Stack2 que usa composição?
qed

5
Eu aprecio esta resposta; no entanto, sinto como se a resposta se desviasse e se aprofundasse mais nas preocupações em torno do design do idioma (e de um pacote específico) do que na resposta à pergunta feita sobre composição versus herança. Sou muito fã de responder à pergunta sobre SO, citando recursos - não vinculando a recursos externos sem fornecer um resumo mais aprofundado do que um somatório de uma linha.
Thomas

5
Mau exemplo, tive que fazer uma pesquisa extra para entender o que Vector e Stack são.
Sam Ramezanli 25/03

Bom, mas o seu exemplo sobre pilha java não é um bom candidato por causa desta herança é uma amostra de má decisão sobre a escolha de herança sobre composição como disse neste artigo: thoughtworks.com/insights/blog/...
Qmaster

212

Composição significa HAS A
HerançaIS A

Example: O carro tem um motor e o carro é um automóvel

Na programação, isso é representado 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.
}

7
Eu mudaria "automóvel" para "veículo motorizado", como em muitas interpretações carros e automóveis são equivalentes.
Nanofarad

5
@hexafraction Concordo que o Veículo provavelmente seria uma escolha melhor, mas ao mesmo tempo - código - ilustrou o ponto muito bem para o que foi solicitado.
Nckbrz 27/03

5
"um motor":-/
Omar Tariq

bem expained. +1. leitura adicional javarevisited.blogspot.in/2015/06/…
roottraveller

@AndreyAkhmetov Automobile pode ter typecampo de TypeEnum
Ojonugwa Jude Ochalifu

42

Como a herança pode ser perigosa?

Vamos dar um exemplo

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

1) Como está claro no código acima, a Classe Y possui um acoplamento muito forte com a classe X. Se algo mudar na superclasse X, Y poderá quebrar drasticamente. Suponha que, no futuro, a classe X implemente um método de trabalho com a assinatura abaixo

public int work(){
}

A alteração é feita na classe X, mas tornará a classe Y compilável. Portanto, esse tipo de dependência pode subir para qualquer nível e pode ser muito perigoso. Toda vez que a superclasse pode não ter total visibilidade para codificar dentro de todas as suas subclasses, a subclasse pode ficar observando o que está acontecendo na superclasse o tempo todo. Portanto, precisamos evitar esse acoplamento forte e desnecessário.

Como a composição resolve esse problema?

Vamos ver revisando o mesmo exemplo

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

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

Aqui, estamos criando referência da classe X na classe Y e invocando o método da classe X, criando uma instância da classe X. Agora todo esse forte acoplamento se foi. Superclasse e subclasse são altamente independentes uma da outra agora. As aulas podem fazer livremente alterações perigosas na situação de herança.

2) Segunda vantagem muito boa da composição, pois fornece flexibilidade de chamada de método, por exemplo:

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

public class Test{    
    R r;    
}

Na classe Teste, usando a referência r, posso chamar métodos da classe X e da classe Y. Essa flexibilidade nunca esteve presente na herança

3) Outra grande vantagem: Teste de unidade

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

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

No exemplo acima, se o estado da instância x não for conhecido, ele poderá ser facilmente zombado usando alguns dados de teste e todos os métodos poderão ser facilmente testados. Isso não foi possível na herança, pois você dependia muito da superclasse para obter o estado da instância e executar qualquer método.

4) Outra boa razão pela qual devemos evitar a herança é que o Java não suporta herança múltipla.

Vamos dar um exemplo para entender isso:

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

Bom saber :

  1. composição é facilmente alcançada em tempo de execução, enquanto a herança fornece seus recursos em tempo de compilação

  2. composição também é conhecida como relação HAS-A e herança também é conhecida como relação IS-A

Portanto, crie o hábito de sempre preferir a composição à herança por várias razões acima.


3
concordo, mas dada a sua solução usando a composição .... ainda precisamos fazer um babá, por exemplo, agora superclasse X altere o nome do método de fazer para fazer ... então a subclasse Y também precisará ser mantida (precisa ser alterada também) isso ainda é acoplamento apertado? E como nos livramos disso?
Stwnoverflow 04/11

19

A resposta dada por @Michael Rodrigues não está correta (peço desculpas; não posso comentar diretamente) e pode levar a alguma confusão.

A implementação de interface é uma forma de herança ... quando você implementa uma interface, não está apenas herdando todas as constantes, mas também comprometendo seu objeto com o tipo especificado pela interface; ainda é um " é-um relacionamento ". Se um carro implementa o Fillable , o carro " é-a " Fillable e pode ser usado em seu código onde quer que você use um Fillable .

A composição é fundamentalmente diferente da herança. Ao usar a composição, você está (como as outras respostas observam) fazendo um relacionamento " tem um " entre dois objetos, em oposição ao relacionamento " é um " que você cria ao usar a herança .

Portanto, a partir dos exemplos de carros nas outras perguntas, se eu quisesse dizer que um carro " tem um tanque de gasolina", usaria a composição da seguinte maneira:

public class Car {

private GasTank myCarsGasTank;

}

Espero que isso esclareça qualquer mal-entendido.


17

A herança traz à tona a relação IS-A . Composição traz à tona a relação HAS-A . O padrão de estratégia explica que a composição deve ser usada nos casos em que há famílias de algoritmos que definem um comportamento específico.
Exemplo clássico de uma classe de pato que implementa um comportamento de vôo.

public interface Flyable{
 public void fly();
}

public class Duck {
 Flyable fly;

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

Assim, podemos ter várias classes que implementam o vôo, por exemplo:

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

Se fosse por herança, teríamos duas classes diferentes de pássaros que implementam a função de voar repetidamente. Portanto, herança e composição são completamente diferentes.


7

A composição é exatamente como parece - você cria um objeto conectando partes.

EDITAR o restante desta resposta é erroneamente baseado na seguinte premissa.
Isso é realizado com interfaces.
Por exemplo, usando o Carexemplo acima,

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

Assim, com alguns componentes teóricos padrão, você pode construir seu objeto. É seu trabalho preencher como a Houseprotege seus ocupantes e como a Carprotege seus ocupantes.

A herança é como o contrário. Você começa com um objeto completo (ou semi-completo) e substitui ou substitui os vários bits que deseja alterar.

Por exemplo, MotorVehiclepode vir com um Fuelablemétodo e Drivemétodo Você pode deixar o método Fuel como está, porque é o mesmo que encher uma moto e um carro, mas você pode substituir o Drivemétodo porque a Moto dirige de maneira muito diferente de a Car.

Com a herança, algumas classes já estão completamente implementadas e outras possuem métodos que você é forçado a substituir. Com a composição, nada é dado a você. (mas você pode implementar as interfaces chamando métodos em outras classes se houver algo por aí).

A composição é vista como mais flexível, porque se você tiver um método como iUsesFuel, poderá ter um método em outro lugar (outra classe, outro projeto) que se preocupe apenas em lidar com objetos que podem ser abastecidos, independentemente de se tratar de um carro, barco, fogão, churrasco, etc. As interfaces determinam que as classes que dizem que implementam essa interface tenham realmente os métodos que essa interface trata. Por exemplo,

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

então você pode ter um método em outro lugar

private void FillHerUp(iFuelable : objectToFill) {

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

        objectToFill.AddSomeFuel();
   }

Exemplo estranho, mas mostra que esse método não se importa com o que está preenchendo, porque o objeto implementa iUsesFuel, pode ser preenchido. Fim da história.

Se você usasse Herança em vez disso, precisaria de FillHerUpmétodos diferentes para lidar com , MotorVehiclese Barbecues, a menos que tenha algum objeto base "ObjectThatUsesFuel" bastante estranho do qual herdar.


As convenções Java afirmam que os nomes de classe e interface são gravados ThisCase, não dentro camelCase. Portanto, é melhor nomear suas interfaces IDrivable, etc. Você pode não precisar do "I" se reagrupar todas as suas interfaces em um pacote corretamente.
ThePyroEagle

6

Composição e herança são iguais?

Eles não são iguais.

Composição : Permite que um grupo de objetos seja tratado da mesma maneira que uma única instância de um objeto. A intenção de um composto é "compor" objetos em estruturas de árvore para representar hierarquias de partes inteiras

Herança : uma classe herda campos e métodos de todas as suas superclasses, sejam diretas ou indiretas. Uma subclasse pode substituir os métodos herdados ou pode ocultar campos ou métodos herdados.

Se eu quiser implementar o padrão de composição, como posso fazer isso em Java?

O artigo da Wikipedia é bom o suficiente para implementar o padrão composto em java.

insira a descrição da imagem aqui

Principais Participantes:

Componente :

  1. É a abstração para todos os componentes, incluindo os compostos
  2. Declara a interface para objetos na composição

Folha :

  1. Representa objetos de folha na composição
  2. Implementa todos os métodos de componentes

Composto :

  1. Representa um componente composto (componente que tem filhos)
  2. Implementa métodos para manipular crianças
  3. Implementa todos os métodos de componentes, geralmente delegando-os a seus filhos

Exemplo de código para entender o padrão Composite :

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

}

resultado:

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

Explicação:

  1. Parte é uma folha
  2. O carro contém muitas peças
  3. Partes diferentes do carro foram adicionadas ao carro
  4. O preço do carro = soma de (preço de cada parte )

Consulte a pergunta abaixo para obter os prós e contras da composição e herança.

Prefere composição sobre herança?


Também faz sentido implementar peças para a classe automóvel. Não será bom se você usá-lo como composição
bhalkian

4

como outro exemplo, considere uma classe de carro, isso seria um bom uso da composição, um carro "teria" um motor, uma transmissão, pneus, assentos etc. Não estenderia nenhuma dessas classes.


4

Composição é onde algo é composto de partes distintas e tem uma forte relação com essas partes. Se a parte principal morre, o mesmo ocorre com as outras, elas não podem ter vida própria. Um exemplo aproximado é o corpo humano. Retire o coração e todas as outras partes morrem.

A herança é onde você apenas pega algo que já existe e o usa. Não há um relacionamento forte. Uma pessoa pode herdar a propriedade de seu pai, mas pode ficar sem ela.

Como não conheço Java, não posso fornecer um exemplo, mas posso fornecer uma explicação dos conceitos.


3

A herança entre duas classes, onde uma classe estende outra classe, estabelece " IS A ".

A composição do outro lado contém uma instância de outra classe na sua classe e estabelece um relacionamento " Tem um ". A composição em java é útil, pois tecnicamente facilita a herança múltipla.


3

Na agregação simples de palavras, significa que possui um relacionamento.

A composição é um caso especial de agregação . De uma maneira mais específica, uma agregação restrita é chamada composição. Quando um objeto contém o outro objeto, se o objeto contido não puder existir sem a existência do objeto contêiner, ele será chamado de composição. Exemplo: Uma turma contém alunos. Um aluno não pode existir sem uma aula. Existe composição entre turma e alunos.

Por que usar agregação

Reutilização de código

Quando usar agregação

A reutilização de código também é melhor alcançada por agregação quando não há um navio de Relação

Herança

A herança é um relacionamento entre pais e filhos A herança significa que é um relacionamento

A herança em java é um mecanismo no qual um objeto adquire todas as propriedades e comportamentos do objeto pai.

Usando herança no Java 1 Code Reusability. 2 Adicione Recurso Extra na Classe Criança, bem como Substituição de Método (para que o polimorfismo em tempo de execução possa ser alcançado).


1

Embora Inheritance e Composition forneçam capacidade de reutilização de código, a principal diferença entre Composition e Herança em Java é que o Composition permite a reutilização de código sem estendê-lo, mas para Herança você deve estender a classe para qualquer reutilização de código ou funcionalidade. Outra diferença que resulta desse fato é que, ao usar o Composition, você pode reutilizar o código até para a classe final que não é extensível, mas a Herança não pode reutilizar o código nesses casos. Além disso, usando Composition, você pode reutilizar o código de muitas classes, pois elas são declaradas apenas como uma variável de membro, mas com o Herança, você pode reutilizar o formulário do código apenas uma classe, porque em Java você pode estender apenas uma classe, porque o recurso Herança múltipla não é suportado em Java. . Você pode fazer isso em C ++, porque uma classe pode estender mais de uma classe. BTW, você deve sempreprefiro Composition a Herança em Java , não sou apenas eu, mas até Joshua Bloch sugeriu em seu livro


1
Por que eu deveria "adotar Composição sobre Herança "? Eles são conceitos diferentes e são usados ​​para diferentes propósitos. Francamente, não vejo como você poderia ir de um para o outro.
Kröw 22/05/19

1

Eu acho que este exemplo explica claramente as diferenças entre herança e composição .

Neste exemplo, o problema é resolvido usando herança e composição. O autor presta atenção ao fato de que; na herança , uma mudança na superclasse pode causar problemas na classe derivada, que a herdam.

Também é possível ver a diferença na representação quando você usa uma UML para herança ou composição.

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


1
As respostas somente de link são desencorajadas porque ficam obsoletas. Inclua pelo menos as informações relevantes mais importantes no link em sua resposta.
Scott Solmer

1

Heranças versus Composição.

Heranças e composição são usadas para reutilização e extensão do comportamento da classe.

As heranças usadas principalmente em um modelo de programação de algoritmos de família, como o tipo de relação IS-A, significam tipos semelhantes de objetos. Exemplo.

  1. Espanador é um carro
  2. Safari é um carro

Estes são pertencem à família Car.

A composição representa o tipo de relacionamento HAS-A. Mostra a capacidade de um objeto como o Duster tem cinco marchas, o Safari tem quatro marchas etc. Sempre que precisarmos estender a capacidade de uma classe existente, use a composição. Exemplo , precisamos adicionar mais uma engrenagem no objeto Duster, em seguida, precisamos criar mais um objeto de engrenagem e compor ao objeto Duster.

Não devemos fazer as alterações na classe base até / a menos que todas as classes derivadas precisem dessas funcionalidades. Nesse cenário, devemos usar Composition.Such

classe A Derivado pela classe B

Classe A Derivada pela Classe C

Classe A Derivada pela Classe D.

Quando adicionamos qualquer funcionalidade na classe A, ela fica disponível para todas as subclasses, mesmo quando as classes C e D não exigem essa funcionalidade. Para esse cenário, precisamos criar uma classe separada para essa funcionalidade e compor a classe necessária ( aqui é a classe B).

Abaixo está o exemplo:

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

0

Composição significa criar um objeto para uma classe que tenha relação com essa classe específica. Suponha que o aluno tenha relação com as contas;

Uma herança é que esta é a classe anterior com o recurso estendido. Isso significa que esta nova classe é a classe antiga com algum recurso estendido. Suponha que Aluno seja Aluno, mas Todos os Alunos são Humanos. Portanto, existe um relacionamento com aluno e humano. Isso é herança.


0

Não, ambos são diferentes. A composição segue o relacionamento "HAS-A" e a herança segue o relacionamento "IS-A". O melhor exemplo de composição foi o padrão estratégico.


2
Sua única qualidade de comentário
Mathews Sunny

Você acabou de comentar a mesma coisa que a resposta aceita disse 7 anos antes de você?
Anjil Dhamala

0

Herança significa reutilizar a funcionalidade completa de uma classe. Aqui, minha classe precisa usar todos os métodos da superclasse e minha classe será totalmente acoplada à superclasse e o código será duplicado em ambas as classes em caso de herança.

Mas podemos superar todos esses problemas quando usamos a composição para conversar com outra classe. composição está declarando um atributo de outra classe na minha classe com a qual queremos conversar. e que funcionalidade queremos dessa classe que podemos obter usando esse atributo.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.