JavaRush /Java Blog /Random-IT /Ereditarietà vs composizione in Java
dio
Livello 16
Москва

Ereditarietà vs composizione in Java

Pubblicato nel gruppo Random-IT
Questo articolo illustra i concetti di ereditarietà e composizione in Java. Il primo esempio illustra l'ereditarietà e quindi mostra come migliorare la progettazione dell'ereditarietà utilizzando la composizione. Alla fine riassumeremo come scegliere tra di loro. Ereditarietà vs composizione in Java - 1

1. Eredità

Ipotizziamo di avere una classe Insect(inglese insetto) Questa classe contiene due metodi: 1. move()(dall'inglese move) e 2. attack()(dall'inglese 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");
	}
}
Ora vuoi definire una classe Bee(English bee), che è uno dei tipi Insect, ma ha implementazioni diverse attack()di e move(). Questo può essere fatto usando l'ereditarietà:
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();
	}
}
Il diagramma della gerarchia delle classi è abbastanza semplice: Ereditarietà vs composizione in Java - 2Risultato dell'esecuzione:
Fly
Fly
Attack
"Fly" viene digitato due volte, quindi il metodo move()viene chiamato due volte. Ma dovrebbe essere chiamato solo una volta. Il problema è causato da super.attack(). Il metodo attack ()chiama un metodo move()di classe Insect. Quando una sottoclasse chiama super.attack (), chiama anche il metodo sovrascritto move(). Per risolvere il problema possiamo:
  1. Eliminare il attack()metodo della sottoclasse. Ciò renderà la sottoclasse dipendente attack()dall'implementazione del metodo della superclasse. Se attack()attack()la superclasse inizia a utilizzare un metodo diverso per lo spostamento, anche la sottoclasse dovrà cambiare. Questo è un cattivo incapsulamento.
  2. Riscrivi il metodo attack()come segue:

    public void attack() {
    	move();
    	System.out.println("Attack");
    }
  3. Ciò garantisce il risultato corretto perché la sottoclasse non dipende più dalla superclasse. Tuttavia, il codice è un duplicato della superclasse. (il metodo attack()fa cose più complesse del semplice output di una stringa). Questa non è una buona progettazione del software e non dovrebbe esserci codice duplicato.

Questa progettazione dell'ereditarietà è errata perché la sottoclasse dipende dai dettagli di implementazione della sua superclasse. Se la superclasse cambia, la sottoclasse non funzionerà correttamente.

2. Composizione

È possibile utilizzare la composizione anziché l'ereditarietà. Diamo un'occhiata a una soluzione che lo utilizza. La funzione attack()è astratta come interfaccia.
interface Attack {
	public void move();
	public void attack();
}
È possibile definire diversi tipi di attacco implementando l'interfaccia 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);
	}
}
Poiché la funzione di attacco è esterna, la classe Insectnon la contiene più.
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;
	}
}
Classe Bee(dall'inglese Bee), come un tipo Insectpuò attaccare.
// 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();
	}
}
Diagramma delle classi: Ereditarietà vs composizione in 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();
	}
}
Risultato dell'esecuzione:
fly
move
fly
sting

3. Quando utilizzare questi approcci?

I seguenti 2 punti possono aiutarti a decidere tra ereditarietà e composizione:
  1. Se hai a che fare con una relazione tra classi della forma "IS" e una classe desidera fornire tutte le sue interfacce a un'altra classe, è preferibile l'ereditarietà.
  2. se la relazione è "HAS", è preferibile la composizione.
Pertanto, l'ereditarietà e la composizione hanno le loro applicazioni e vale la pena comprenderne i meriti. Collegamenti:
  1. Bloch, Giosuè. Java efficace. Pearson Education India, 2008
  2. https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
  3. https://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--wich-one-should...
Link all'articolo originale Tradotto
Commenti
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION