מאמר זה ממחיש את המושגים של ירושה והרכב בג'אווה. הדוגמה הראשונה מדגימה ירושה ולאחר מכן מראה כיצד לשפר את עיצוב הירושה באמצעות קומפוזיציה. נסכם בסיום כיצד לבחור ביניהם.
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
(דבורה אנגלית), שהיא אחת מהסוגים 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
"זבוב" מוקלד פעמיים, ולכן השיטה move()
נקראת פעמיים. אבל צריך לקרוא לזה רק פעם אחת. הבעיה נגרמת על ידי ה super.attack()
. השיטה attack ()
קוראת ל- move()
class method 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", אז ההרכב מועדף.
- בלוך, יהושע. Java יעיל. פירסון חינוך הודו, 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