این مقاله مفاهیم وراثت و ترکیب در جاوا را نشان می دهد. مثال اول وراثت را نشان می دهد و سپس نحوه بهبود طراحی وراثت با استفاده از ترکیب را نشان می دهد. در پایان نحوه انتخاب بین آنها را خلاصه خواهیم کرد.
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 ()
، متد overrid را نیز فراخوانی می کند 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();
}
انواع مختلف حمله را می توان با پیاده سازی رابط 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" باشد، ترکیب ترجیح داده می شود.
- بلوخ، جاشوا. جاوا موثر آموزش پیرسون هند، 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