บทความนี้แสดงแนวคิดเกี่ยวกับการสืบทอดและองค์ประกอบใน Java ตัวอย่างแรกแสดงให้เห็นถึงการสืบทอด จากนั้นจะแสดงวิธีปรับปรุงการออกแบบการสืบทอดโดยใช้องค์ประกอบ เราจะสรุปวิธีการเลือกระหว่างพวกเขาในตอนท้าย
ผลลัพธ์การดำเนินการ:
1. มรดก
สมมติว่าเรามีคลาสInsect
(แมลงภาษาอังกฤษ) คลาสนี้มีสองวิธี: 1. move()
(จากภาษาอังกฤษย้าย) และ 2. 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");
}
}
ตอนนี้คุณต้องการกำหนดคลาสBee
(English bee) ซึ่งเป็นหนึ่งในประเภทInsect
แต่มีการใช้งานที่แตกต่างกันattack()
ของmove()
และ สามารถทำได้โดยใช้การสืบทอด:
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();
}
}
แผนภาพลำดับชั้นของคลาสค่อนข้างง่าย: 
Fly
Fly
Attack
พิมพ์ "Fly" สองครั้ง ดังนั้นmove()
จึงเรียกเมธอดนี้สองครั้ง แต่ควรจะเรียกเพียงครั้งเดียว ปัญหามีสาเหตุมาจากsuper.attack()
. วิธีการattack ()
เรียกวิธีmove()
การเรียน Insect
เมื่อคลาสย่อยเรียกsuper.attack ()
มันจะเรียกเมธอดที่ถูกแทนที่move()
ด้วย เพื่อแก้ไขปัญหาเราสามารถ:
- กำจัด
attack()
วิธีคลาสย่อย สิ่งนี้จะทำให้คลาสย่อยขึ้นอยู่กับattack()
การนำเมธอดของซูเปอร์คลาสไปใช้ หากattack()attack()
ซูเปอร์คลาสเริ่มใช้วิธีอื่นในการเคลื่อนย้าย คลาสย่อยจะต้องเปลี่ยนด้วย นี่คือการห่อหุ้มที่ไม่ดี -
เขียนวิธีการใหม่
attack()
ดังต่อไปนี้:public void attack() { move(); System.out.println("Attack"); }
-
สิ่งนี้รับประกันผลลัพธ์ที่ถูกต้องเนื่องจากคลาสย่อยไม่ขึ้นอยู่กับซูเปอร์คลาสอีกต่อไป อย่างไรก็ตาม รหัสนี้ซ้ำกับซูเปอร์คลาส (เมธอดนี้
attack()
ทำสิ่งที่ซับซ้อนมากกว่าแค่ส่งออกสตริง) นี่ไม่ใช่การออกแบบซอฟต์แวร์ที่ดีและไม่ควรมีโค้ดที่ซ้ำกัน
2. องค์ประกอบ
คุณสามารถใช้องค์ประกอบแทนการสืบทอดได้ มาดูวิธีแก้ปัญหาการใช้งานกัน ฟังก์ชันนี้attack()
ถูกสรุปเป็นอินเทอร์เฟซ
interface Attack {
public void move();
public void 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);
}
}
เนื่องจากฟังก์ชันการโจมตีเป็นแบบภายนอก คลาสจึงInsect
ไม่มีฟังก์ชันนี้อีกต่อไป
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;
}
}
คลาสBee
(จากภาษาอังกฤษ Bee) ว่าประเภทInsect
สามารถโจมตี ได้อย่างไร
// 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();
}
}
แผนภาพชั้นเรียน: 
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();
}
}
ผลการดำเนินการ:
fly
move
fly
sting
3. เมื่อใดจึงควรใช้แนวทางเหล่านี้?
2 ประเด็นต่อไปนี้สามารถช่วยคุณตัดสินใจได้ระหว่างการสืบทอดและองค์ประกอบ:- หากคุณกำลังจัดการกับความสัมพันธ์ระหว่างคลาสในรูปแบบ "IS" และคลาสต้องการจัดเตรียมอินเทอร์เฟซทั้งหมดให้กับคลาสอื่น การสืบทอดจะดีกว่า
- ถ้าความสัมพันธ์คือ "HAS" แสดงว่าองค์ประกอบเป็นที่ต้องการ
- โบลช, โจชัว. ชวาที่มีประสิทธิภาพ เพียร์สันเอ็ดดูเคชั่นอินเดีย, 2551
- https://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
- https://www.javaworld.com/article/2076814/core-java/inheritance-versus-composition--who-one-should...
GO TO FULL VERSION