Dieser Artikel veranschaulicht die Konzepte der Vererbung und Zusammensetzung in Java. Das erste Beispiel demonstriert die Vererbung und zeigt dann, wie das Vererbungsdesign mithilfe von Komposition verbessert werden kann. Am Ende werden wir zusammenfassen, wie Sie zwischen ihnen wählen können.
1. Vererbung
Nehmen wir an, wir haben eine KlasseInsect
(engl. Insekt). Diese Klasse enthält zwei Methoden: 1. move()
(von englisch move) und 2. attack()
(von englisch 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");
}
}
Nun möchten Sie eine Klasse (englisch bee) definieren Bee
, die zu den Typen gehört Insect
, aber unterschiedliche Implementierungen attack()
von und hat move()
. Dies kann durch Vererbung erfolgen:
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();
}
}
Das Klassenhierarchiediagramm ist recht einfach: Ausführungsergebnis:
Fly
Fly
Attack
„Fly“ wird zweimal eingegeben, daher move()
wird die Methode zweimal aufgerufen. Es sollte aber nur einmal aufgerufen werden. Das Problem wird durch die verursacht super.attack()
. Die Methode attack ()
ruft eine move()
Klassenmethode auf Insect
. Wenn eine Unterklasse aufruft super.attack ()
, ruft sie auch die überschriebene Methode auf move()
. Um das Problem zu beheben, können wir:
- Eliminieren Sie die
attack()
Unterklassenmethode. Dadurch wird die Unterklasse von derattack()
Methodenimplementierung der Oberklasse abhängig. Wennattack()attack()
die Oberklasse beginnt, eine andere Methode zum Verschieben zu verwenden, muss sich auch die Unterklasse ändern. Das ist eine schlechte Kapselung. -
Schreiben Sie die Methode
attack()
wie folgt um:public void attack() { move(); System.out.println("Attack"); }
-
Dies garantiert das korrekte Ergebnis, da die Unterklasse nicht mehr von der Oberklasse abhängt. Der Code ist jedoch ein Duplikat der Superklasse. (Die Methode
attack()
erledigt komplexere Dinge als nur die Ausgabe einer Zeichenfolge). Dies ist kein gutes Softwaredesign und es sollte keinen doppelten Code geben.
2. Zusammensetzung
Sie können Zusammensetzung anstelle von Vererbung verwenden. Schauen wir uns eine Lösung an, die es verwendet. Die Funktionattack()
wird als Schnittstelle abstrahiert.
interface Attack {
public void move();
public void attack();
}
Durch die Implementierung der Attack-Schnittstelle können verschiedene Angriffsarten definiert werden.
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);
}
}
Da die Angriffsfunktion extern ist, Insect
enthält die Klasse sie nicht mehr.
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;
}
}
Klasse Bee
(von engl. Bee), wie eine Art Insect
angreifen kann.
// 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();
}
}
Klassen Diagramm:
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();
}
}
Ausführungsergebnis:
fly
move
fly
sting
3. Wann sollten diese Ansätze eingesetzt werden?
Die folgenden 2 Punkte können Ihnen bei der Entscheidung zwischen Vererbung und Zusammensetzung helfen:- Wenn es sich um eine Beziehung zwischen Klassen der Form „IS“ handelt und eine Klasse alle ihre Schnittstellen einer anderen Klasse zur Verfügung stellen möchte, ist die Vererbung vorzuziehen.
- Wenn die Beziehung „HAS“ ist, wird die Zusammensetzung bevorzugt.
- Bloch, Joshua. Effektives Java. Pearson Education India, 2008
- https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
- https://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--which-one-should...
GO TO FULL VERSION