JavaRush /وبلاگ جاوا /Random-FA /وراثت در مقابل ترکیب در جاوا
dio
مرحله
Москва

وراثت در مقابل ترکیب در جاوا

در گروه منتشر شد
این مقاله مفاهیم وراثت و ترکیب در جاوا را نشان می دهد. مثال اول وراثت را نشان می دهد و سپس نحوه بهبود طراحی وراثت با استفاده از ترکیب را نشان می دهد. در پایان نحوه انتخاب بین آنها را خلاصه خواهیم کرد. وراثت در مقابل ترکیب در جاوا - 1

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();
	}
}
نمودار سلسله مراتب کلاس بسیار ساده است: وراثت در مقابل ترکیب در جاوا - 2نتیجه اجرا:
Fly
Fly
Attack
"Fly" دو بار تایپ می شود، بنابراین روش move()دو بار فراخوانی می شود. اما فقط یکبار باید تماس گرفته شود. مشکل ناشی از super.attack(). متد attack ()یک متد move()کلاس را فراخوانی می کند Insect. هنگامی که یک زیر کلاس فراخوانی می کند super.attack ()، متد overrid را نیز فراخوانی می کند move(). برای رفع مشکل می توانیم:
  1. روش زیر کلاس را حذف کنید attack(). این باعث می شود که زیر کلاس به attack()پیاده سازی متد سوپرکلاس وابسته باشد. اگر attack()attack()سوپرکلاس شروع به استفاده از روش دیگری برای جابجایی کند، کلاس فرعی نیز باید تغییر کند. این کپسولاسیون بد است.
  2. روش را attack()به صورت زیر بازنویسی کنید:

    public void attack() {
    	move();
    	System.out.println("Attack");
    }
  3. این نتیجه صحیح را تضمین می کند زیرا زیر کلاس دیگر به سوپرکلاس وابسته نیست. با این حال، کد تکراری از سوپرکلاس است. (این روش 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();
	}
}
نمودار کلاس: وراثت در مقابل ترکیب در جاوا - 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();
	}
}
نتیجه اجرا:
fly
move
fly
sting

3. چه زمانی از این رویکردها استفاده کنیم؟

2 نکته زیر می تواند به شما در تصمیم گیری بین وراثت و ترکیب کمک کند:
  1. اگر با یک رابطه بین کلاس هایی از فرم "IS" سر و کار دارید و کلاسی می خواهد تمام رابط های خود را به کلاس دیگری ارائه دهد، ارث بری ارجح است.
  2. اگر رابطه "HAS" باشد، ترکیب ترجیح داده می شود.
بنابراین، وراثت و ترکیب کاربردهای خاص خود را دارد و ارزش درک شایستگی آنها را دارد. پیوندها:
  1. بلوخ، جاشوا. جاوا موثر آموزش پیرسون هند، 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...
پیوند به مقاله اصلی ترجمه شده است
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION