JavaRush /Blogue Java /Random-PT /Herança vs composição em Java
dio
Nível 16
Москва

Herança vs composição em Java

Publicado no grupo Random-PT
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. Herança vs Composição em Java - 1

1. Herança

Vamos supor que temos uma classe Insect(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: Herança vs Composição em Java - 2Resultado 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:
  1. Elimine o attack()método da subclasse. Isto tornará a subclasse dependente da attack()implementação do método da superclasse. Se attack()attack()a superclasse começar a usar um método diferente para movimentação, a subclasse também precisará mudar. Este é um encapsulamento ruim.
  2. Reescreva o método attack()da seguinte forma:

    public void attack() {
    	move();
    	System.out.println("Attack");
    }
  3. 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.

Esse projeto de herança é ruim porque a subclasse depende dos detalhes de implementação de sua superclasse. Se a superclasse mudar, a subclasse não funcionará corretamente.

2. Composição

Você pode usar composição em vez de herança. Vejamos uma solução usando-o. A função attack()é 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 Insectnã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 Insectpode 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: Herança vs Composição em Java - 3
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:
  1. 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.
  2. se o relacionamento for "HAS", então a composição é preferida.
Assim, herança e composição têm aplicações próprias e vale a pena compreender seus méritos. Links:
  1. Bloch, Josué. Java eficaz. Pearson Education Índia, 2008
  2. https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
  3. https://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--qual-um-deveria...
Link para o artigo original traduzido
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION