JavaRush /Blog Java /Random-FR /Héritage vs composition en Java
dio
Niveau 16
Москва

Héritage vs composition en Java

Publié dans le groupe Random-FR
Cet article illustre les concepts d'héritage et de composition en Java. Le premier exemple illustre l’héritage, puis montre comment améliorer la conception de l’héritage à l’aide de la composition. Nous résumerons comment choisir entre eux à la fin. Héritage vs composition en Java - 1

1. Héritage

Supposons que nous ayons une classe Insect(insecte anglais). Cette classe contient deux méthodes : 1. move()(de l'anglais move) et 2. attack()(de l'anglais 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");
	}
}
Vous souhaitez maintenant définir une classe Bee(English bee), qui est l'un des types Insect, mais qui a des implémentations différentes attack()de et move(). Cela peut être fait en utilisant l'héritage :
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();
	}
}
Le diagramme de hiérarchie des classes est assez simple : Héritage vs composition en Java - 2Résultat de l'exécution :
Fly
Fly
Attack
"Fly" est tapé deux fois, donc la méthode move()est appelée deux fois. Mais il ne faut l'appeler qu'une seule fois. Le problème est causé par le super.attack(). La méthode attack ()appelle une méthode move()de classe Insect. Lorsqu'une sous-classe appelle super.attack (), elle appelle également la méthode remplacée move(). Pour résoudre le problème, nous pouvons :
  1. Éliminez la attack()méthode des sous-classes. Cela rendra la sous-classe dépendante de attack()l'implémentation de la méthode de la superclasse. Si attack()attack()la superclasse commence à utiliser une méthode de déplacement différente, la sous-classe devra également changer. C'est une mauvaise encapsulation.
  2. Réécrivez la méthode attack()comme suit :

    public void attack() {
    	move();
    	System.out.println("Attack");
    }
  3. Cela garantit le résultat correct car la sous-classe ne dépend plus de la superclasse. Cependant, le code est un double de la superclasse. (la méthode attack()fait des choses plus complexes que la simple sortie d'une chaîne). Ce n’est pas une bonne conception logicielle et il ne devrait y avoir aucun code en double.

Cette conception d'héritage est mauvaise car la sous-classe dépend des détails d'implémentation de sa superclasse. Si la superclasse change, la sous-classe ne fonctionnera pas correctement.

2.Composition

Vous pouvez utiliser la composition au lieu de l'héritage. Regardons une solution l'utilisant. La fonction attack()est abstraite en tant qu'interface.
interface Attack {
	public void move();
	public void attack();
}
Différents types d'attaques peuvent être définis en implémentant l'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);
	}
}
La fonction d’attaque étant externe, la classe Insectne la contient plus.
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(de l'anglais Bee), comment un type Insectpeut attaquer.
// 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();
	}
}
Diagramme de classes : Héritage vs composition en 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();
	}
}
Résultat de l'exécution :
fly
move
fly
sting

3. Quand utiliser ces approches ?

Les 2 points suivants peuvent vous aider à choisir entre succession et composition :
  1. Si vous avez affaire à une relation entre classes de la forme "IS" et qu'une classe souhaite fournir toutes ses interfaces à une autre classe, alors l'héritage est préférable.
  2. si la relation est « HAS », alors la composition est préférée.
Ainsi, l’héritage et la composition ont leurs propres applications et il vaut la peine d’en comprendre les mérites. Liens:
  1. Bloch, Josué. Java efficace. Pearson Education Inde, 2008
  2. https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
  3. https://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--which-one-should...
Lien vers l'article original Traduit
Commentaires
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION