Este artigo ilustra os conceitos de herança e composição em Java. O primeiro exemplo demonstra herança e depois mostra como melhorar o design de herança usando composição. Resumiremos como escolher entre eles no final.
1. Herança
Vamos supor que temos uma classeInsect
(inglês inseto) Esta classe contém dois métodos: 1. move()
(do inglês move) e 2. attack()
(do inglês attack)
class Insect {
private int size;
private String color;
public Insect(int size, String color) {
this.size = size;
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void move() {
System.out.println("Move");
}
public void attack() {
move(); //assuming an insect needs to move before attacking
System.out.println("Attack");
}
}
Agora você deseja definir uma classe Bee
(abelha inglesa), que é um dos tipos Insect
, mas possui implementações diferentes attack()
de e move()
. Isso pode ser feito usando herança:
class Bee extends Insect {
public Bee(int size, String color) {
super(size, color);
}
public void move() {
System.out.println("Fly");
}
public void attack() {
move();
super.attack();
}
}
public class InheritanceVSComposition {
public static void main(String[] args) {
Insect i = new Bee(1, "red");
i.attack();
}
}
O diagrama de hierarquia de classes é bastante simples: Resultado da execução:
Fly
Fly
Attack
"Fly" é digitado duas vezes, então o método move()
é chamado duas vezes. Mas só deve ser chamado uma vez. O problema é causado pelo super.attack()
. O método attack ()
chama um método move()
de classe Insect
. Quando uma subclasse chama super.attack ()
, ela também chama o método substituído move()
. Para resolver o problema, podemos:
- Elimine o
attack()
método da subclasse. Isto tornará a subclasse dependente daattack()
implementação do método da superclasse. Seattack()attack()
a superclasse começar a usar um método diferente para movimentação, a subclasse também precisará mudar. Este é um encapsulamento ruim. -
Reescreva o método
attack()
da seguinte forma:public void attack() { move(); System.out.println("Attack"); }
-
Isto garante o resultado correto porque a subclasse não depende mais da superclasse. No entanto, o código é uma duplicata da superclasse. (o método
attack()
faz coisas mais complexas do que apenas gerar uma string). Este não é um bom design de software e não deve haver código duplicado.
2. Composição
Você pode usar composição em vez de herança. Vejamos uma solução usando-o. A funçãoattack()
é abstraída como uma interface.
interface Attack {
public void move();
public void attack();
}
Diferentes tipos de ataque podem ser definidos implementando a interface Attack.
class AttackImpl implements Attack {
private String move;
private String attack;
public AttackImpl(String move, String attack) {
this.move = move;
this.attack = attack;
}
@Override
public void move() {
System.out.println(move);
}
@Override
public void attack() {
move();
System.out.println(attack);
}
}
Como a função de ataque é externa, a classe Insect
não a contém mais.
class Insect {
private int size;
private String color;
public Insect(int size, String color) {
this.size = size;
this.color = color;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
}
Aula Bee
(do English Bee), como o tipo Insect
pode atacar.
// This wrapper class wrap an Attack object
class Bee extends Insect implements Attack {
private Attack attack;
public Bee(int size, String color, Attack attack) {
super(size, color);
this.attack = attack;
}
public void move() {
attack.move();
}
public void attack() {
attack.attack();
}
}
Diagrama de classes:
public class InheritanceVSComposition2 {
public static void main(String[] args) {
Bee a = new Bee(1, "black", new AttackImpl("fly", "move"));
a.attack();
// if you need another implementation of move()
// there is no need to change Insect, we can quickly use new method to attack
Bee b = new Bee(1, "black", new AttackImpl("fly", "sting"));
b.attack();
}
}
Resultado da execução:
fly
move
fly
sting
3. Quando utilizar estas abordagens?
Os 2 pontos a seguir podem ajudá-lo a decidir entre herança e composição:- Se você está lidando com um relacionamento entre classes do formato "IS" e uma classe deseja fornecer todas as suas interfaces para outra classe, então a herança é preferível.
- se o relacionamento for "HAS", então a composição é preferida.
- Bloch, Josué. Java eficaz. Pearson Education Índia, 2008
- https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
- https://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--qual-um-deveria...
GO TO FULL VERSION