JavaRush /وبلاگ جاوا /Random-FA /وراثت چندگانه در جاوا ترکیب در مقابل وراثت
DSergey_Kh
مرحله

وراثت چندگانه در جاوا ترکیب در مقابل وراثت

در گروه منتشر شد

وراثت چندگانه در جاوا

وراثت چندگانه به شما امکان می دهد کلاسی ایجاد کنید که از چندین سوپرکلاس ارث می برد. برخلاف برخی دیگر از زبان های برنامه نویسی شی گرا مانند C++، جاوا اجازه ارث بردن چندگانه از کلاس ها را نمی دهد. جاوا از وراثت چند کلاسه پشتیبانی نمی کند زیرا می تواند منجر به مشکل الماس شود. و به جای جستجوی راه‌هایی برای حل این مشکل، گزینه‌های بهتری وجود دارد که چگونه می‌توانیم به همان نتیجه مانند ارث چندگانه دست یابیم.

مشکل الماس

برای درک آسانتر مشکل الماس، اجازه دهید فرض کنیم که وراثت چندگانه در جاوا پشتیبانی می شود. در این حالت، می‌توانیم یک سلسله مراتب طبقاتی داشته باشیم که در تصویر زیر نشان داده شده است. وراثت چندگانه در جاوا  ترکیب در مقابل وراثت - 1فرض کنید کلاس SuperClassانتزاعی است و متدهایی در آن اعلان شده است. هر دو کلاس بتن ClassAو ClassB.
package com.journaldev.inheritance;
public abstract class SuperClass {
	public abstract void doSomething();
}
package com.journaldev.inheritance;
public class ClassA extends SuperClass{
	@Override
	public void doSomething(){
		System.out.println("doSomething implementation of A");
	}
	//ClassA own method
	public void methodA(){
	}
}
package com.journaldev.inheritance;
public class ClassB extends SuperClass{
	@Override
	public void doSomething(){
		System.out.println("doSomething implementation of B");
	}
	//ClassB specific method
	public void methodB(){
	}
}
حال فرض کنید می خواهیم ClassCآن را پیاده سازی کنیم و از ClassAو به ارث ببریم ClassB.
package com.journaldev.inheritance;
public class ClassC extends ClassA, ClassB{
	public void test(){
		//calling super class method
		doSomething();
	}
}
توجه داشته باشید که متد test()متد superclass را فراخوانی می کند doSomething(). این منجر به ابهام می شود زیرا کامپایلر نمی داند کدام متد سوپرکلاس را اجرا کند. این یک نمودار کلاس الماس شکل است که به آن مشکل الماس می گویند. این دلیل اصلی عدم پشتیبانی جاوا از وراثت چندگانه است. توجه داشته باشید که مشکل فوق با وراثت چند کلاسی فقط می تواند با سه کلاس که حداقل یک متد مشترک دارند اتفاق بیفتد.

وراثت چند رابط

در جاوا، وراثت چندگانه در کلاس ها پشتیبانی نمی شود، اما در رابط ها پشتیبانی می شود. و یک رابط می تواند بسیاری از رابط های دیگر را گسترش دهد. در زیر یک مثال ساده آورده شده است.
package com.journaldev.inheritance;
public interface InterfaceA {
	public void doSomething();
}
package com.journaldev.inheritance;
public interface InterfaceB {
	public void doSomething();
}
توجه داشته باشید که هر دو رابط یک روش را اعلام می کنند. اکنون می‌توانیم یک رابط ایجاد کنیم که هر دوی این رابط‌ها را گسترش دهد، همانطور که در مثال زیر نشان داده شده است.
package com.journaldev.inheritance;
public interface InterfaceC extends InterfaceA, InterfaceB {
	//same method is declared in InterfaceA and InterfaceB both
	public void doSomething();
}
این عالی کار می کند زیرا اینترفیس ها فقط متدها را اعلام می کنند و پیاده سازی در کلاس هایی انجام می شود که رابط را به ارث می برند. بنابراین، هیچ راهی برای به دست آوردن ابهام در وراثت چند رابط وجود ندارد.
package com.journaldev.inheritance;
public class InterfacesImpl implements InterfaceA, InterfaceB, InterfaceC {
	@Override
	public void doSomething() {
		System.out.println("doSomething implementation of concrete class");
	}
	public static void main(String[] args) {
		InterfaceA objA = new InterfacesImpl();
		InterfaceB objB = new InterfacesImpl();
		InterfaceC objC = new InterfacesImpl();

		//all the method calls below are going to same concrete implementation
		objA.doSomething();
		objB.doSomething();
		objC.doSomething();
	}
}
لطفاً توجه داشته باشید که هر زمان که یک روش سوپرکلاس را لغو می‌کنید یا یک روش رابط را پیاده‌سازی می‌کنید، از حاشیه‌نویسی استفاده کنید @Override. اگر بخواهیم از یک تابع methodA()از یک کلاس ClassAو یک تابع methodB()از یک کلاس ClassBدر یک کلاس استفاده کنیم ClassCچه؟ راه حل در استفاده از ترکیب نهفته است. در زیر نسخه ای از کلاس وجود دارد ClassCکه از ترکیب برای تعریف متدهای کلاس و متد doSomething()یکی از اشیا استفاده می کند.
package com.journaldev.inheritance;
public class ClassC{
	ClassA objA = new ClassA();
	ClassB objB = new ClassB();
	public void test(){
		objA.doSomething();
	}
	public void methodA(){
		objA.methodA();
	}
	public void methodB(){
		objB.methodB();
	}
}

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

یکی از بهترین روش‌های برنامه‌نویسی جاوا، «تأیید ترکیب قبل از وراثت» است. ما برخی از جنبه هایی را که به نفع این رویکرد است بررسی خواهیم کرد.
  1. فرض کنید یک سوپرکلاس و یک کلاس داریم که آن را گسترش می دهد:

    package com.journaldev.inheritance;
    public class ClassC{
    	public void methodC(){
    	}
    }
    package com.journaldev.inheritance;
    public class ClassD extends ClassC{
    	public int test(){
    		return 0;
    	}
    }

    کد بالا کامپایل شده و به خوبی کار می کند. اما، اگر پیاده سازی کلاس را ClassCمطابق شکل زیر تغییر دهیم، چه می شود:

    package com.journaldev.inheritance;
    public class ClassC{
    	public void methodC(){
    	}
    	public void test(){
    	}
    }

    توجه داشته باشید که روش test()از قبل در زیر کلاس وجود دارد، اما نوع بازگشت متفاوت است. اکنون کلاس ClassDکامپایل نمی‌شود و اگر از هر IDE استفاده می‌کنید، از شما می‌خواهد که نوع بازگشت را در سوپرکلاس یا زیر کلاس تغییر دهید.

    حال شرایطی را تصور کنید که در آن ما یک سلسله مراتب ارث بری کلاس چند سطحی داریم و به سوپرکلاس دسترسی نداریم. ما چاره ای نخواهیم داشت جز اینکه امضای متد زیر کلاس یا نام آن را برای حذف خطای کامپایل تغییر دهیم. همچنین باید متد subclass را در همه جاهایی که فراخوانی می شود تغییر دهیم. بنابراین، وراثت کد ما را شکننده می کند.

    مشکل فوق هرگز در ترکیب اتفاق نمی افتد و این باعث می شود که ارثی جذابیت بیشتری داشته باشد.

  2. مشکل دیگر وراثت این است که ما تمام متدهای سوپرکلاس را در معرض دید کلاینت قرار می دهیم و اگر سوپرکلاس ما به درستی طراحی نشده باشد و حفره های امنیتی وجود داشته باشد، حتی اگر بهترین پیاده سازی کلاس خود را پیاده سازی کنیم، تحت تاثیر پیاده سازی ضعیف قرار می گیریم. از سوپرکلاس . ترکیب به ما در ارائه دسترسی کنترل‌شده به روش‌های سوپرکلاس کمک می‌کند، در حالی که وراثت کنترلی بر روش‌های سوپرکلاس فراهم نمی‌کند. این نیز یکی از مزایای اصلی ترکیب از ارث است.

  3. مزیت دیگر ترکیب این است که امکان انعطاف پذیری در فراخوانی روش ها را فراهم می کند. پیاده سازی ما از کلاس ClassCارائه شده در بالا بهینه نیست و تضمین می کند که زمان کامپایل با متدی که فراخوانی می شود مرتبط است. با حداقل تغییرات می توانیم فراخوانی متد را انعطاف پذیر و پویا کنیم.

    package com.journaldev.inheritance;
    public class ClassC{
    	SuperClass obj = null;
    	public ClassC(SuperClass o){
    		this.obj = o;
    	}
    	public void test(){
    		obj.doSomething();
    	}
    	public static void main(String args[]){
    		ClassC obj1 = new ClassC(new ClassA());
    		ClassC obj2 = new ClassC(new ClassB());
    
    		obj1.test();
    		obj2.test();
    	}
    }

    نتیجه برنامه ارائه شده در بالا:

    doSomething implementation of A
    doSomething implementation of B

    این انعطاف پذیری در فراخوانی متد با وراثت در دسترس نیست، که مزیت دیگری به انتخاب ترکیب می‌افزاید.

  4. انجام تست واحد با ترکیب آسان‌تر است، زیرا می‌دانیم که از تمام روش‌های سوپرکلاس استفاده می‌کنیم و می‌توانیم آنها را برای آزمون کپی کنیم. در حالی که در وراثت بیشتر به superclass وابسته هستیم و همه روش های superclass را که مورد استفاده قرار می گیرد نمی دانیم. بنابراین ما باید تمام روش های سوپرکلاس را آزمایش کنیم که به دلیل وراثت کار اضافی است.

    در حالت ایده‌آل، ما فقط زمانی باید از وراثت استفاده کنیم که رابطه زیر کلاس به سوپرکلاس به صورت "is" تعریف شده باشد. در تمام موارد دیگر، استفاده از ترکیب توصیه می شود.

نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION