Composição vs Herança é uma das perguntas frequentemente feitas em entrevistas. Você também deve ter ouvido falar para usar Composição em vez de Herança.
Composição vs Herança
Ambas composição e herança são conceitos de programação orientada a objetos. Elas não estão vinculadas a nenhuma linguagem de programação específica, como Java. Antes de compararmos composição sobre herança programaticamente, vamos ter uma definição rápida delas.
Composição
Composição é a técnica de design em programação orientada a objetos para implementar o relacionamento tem-um entre objetos. Em Java, a composição é alcançada usando variáveis de instância de outros objetos. Por exemplo, uma pessoa que tem um trabalho é implementada da seguinte forma em programação orientada a objetos em Java.
package com.journaldev.composition;
public class Job {
// variáveis, métodos, etc.
}
package com.journaldev.composition;
public class Person {
// composição tem um relacionamento
private Job job;
// variáveis, métodos, construtores, etc. orientado a objetos
Herança
Herança é a técnica de design na programação orientada a objetos para implementar a relação é-um entre objetos. A herança em Java é implementada usando a palavra-chave extends. Por exemplo, a relação Gato é um Animal na programação Java será implementada como abaixo.
package com.journaldev.inheritance;
public class Animal {
// variáveis, métodos etc.
}
package com.journaldev.inheritance;
public class Cat extends Animal{
}
Composição sobre Herança
Tanto a composição quanto a herança promovem a reutilização de código por meio de abordagens diferentes. Então, qual escolher? Como comparar composição vs herança. Você deve ter ouvido que na programação você deve favorecer a composição sobre a herança. Vamos ver algumas das razões que o ajudarão a escolher composição vs herança.
-
A herança é fortemente acoplada, enquanto a composição é fracamente acoplada. Vamos supor que temos as classes abaixo com herança.
package com.journaldev.java.examples; public class ClassA { public void foo(){ } } class ClassB extends ClassA{ public void bar(){ } }
Para simplicidade, temos tanto a superclasse quanto a subclasse em um único pacote. Mas geralmente estarão em bases de código separadas. Pode haver muitas classes estendendo a superclasse ClassA. Um exemplo muito comum dessa situação é estender a classe Exception. Agora, vamos dizer que a implementação da ClassA é alterada como abaixo, um novo método bar() é adicionado.
package com.journaldev.java.examples; public class ClassA { public void foo(){ } public int bar(){ return 0; } }
Assim que começar a usar a nova implementação da ClassA, você receberá um erro de compilação na ClassB como
O tipo de retorno é incompatível com ClassA.bar()
. A solução seria alterar o método bar() da superclasse ou da subclasse para torná-los compatíveis. Se você tivesse usado Composição em vez de herança, nunca enfrentaria esse problema. Um exemplo simples da implementação da ClassB usando Composição pode ser o seguinte.class ClassB{ ClassA classA = new ClassA(); public void bar(){ classA.foo(); classA.bar(); } }
-
Não há controle de acesso na herança, enquanto o acesso pode ser restrito na composição. Expondo todos os métodos da superclasse para outras classes que têm acesso à subclasse. Portanto, se um novo método for introduzido ou se houver falhas de segurança na superclasse, a subclasse se torna vulnerável. Já na composição, escolhemos quais métodos utilizar, sendo mais seguro do que a herança. Por exemplo, podemos fornecer a exposição do método ClassA foo() para outras classes usando o código abaixo na ClassB.
class ClassB { ClassA classA = new ClassA(); public void foo(){ classA.foo(); } public void bar(){ } }
Essa é uma das principais vantagens da composição sobre a herança.
-
A composição oferece flexibilidade na invocação de métodos, o que é útil em cenários com várias subclasses. Por exemplo, suponha que tenhamos o seguinte cenário de herança.
abstract class Abs { abstract void foo(); } public class ClassA extends Abs{ public void foo(){ } } class ClassB extends Abs{ public void foo(){ } } class Test { ClassA a = new ClassA(); ClassB b = new ClassB(); public void test(){ a.foo(); b.foo(); } }
E se houver mais subclasses, a composição tornará nosso código feio, tendo uma instância para cada subclasse? Não, podemos reescrever a classe Teste da seguinte maneira.
class Teste { Abs obj = null; Teste(Abs o){ this.obj = o; } public void foo(){ this.obj.foo(); } }
Isso lhe dará a flexibilidade de usar qualquer subclasse com base no objeto usado no construtor.
-
Mais um benefício da composição sobre a herança é o escopo de teste. O teste de unidade é fácil na composição porque sabemos quais métodos estamos usando de outra classe. Podemos simulá-lo para teste, enquanto na herança dependemos muito da superclasse e não sabemos quais métodos da superclasse serão usados. Portanto, teremos que testar todos os métodos da superclasse. Isso é trabalho extra e precisamos fazer isso desnecessariamente por causa da herança.
Isso é tudo para composição versus herança. Você tem motivos suficientes para escolher a composição em vez da herança. Use herança apenas quando tiver certeza de que a superclasse não será alterada, caso contrário, opte pela composição.
Source:
https://www.digitalocean.com/community/tutorials/composition-vs-inheritance