JavaRush /จาวาบล็อก /Random-TH /การสืบทอดหลายรายการใน Java องค์ประกอบเทียบกับมรดก
DSergey_Kh
ระดับ

การสืบทอดหลายรายการใน Java องค์ประกอบเทียบกับมรดก

เผยแพร่ในกลุ่ม

การสืบทอดหลายรายการใน Java

การสืบทอดหลายรายการทำให้คุณสามารถสร้างคลาสที่สืบทอดมาจากซูเปอร์คลาสหลายรายการได้ ไม่เหมือนกับภาษาการเขียนโปรแกรมเชิงวัตถุยอดนิยมอื่นๆ เช่น C++ Java ไม่อนุญาตให้มีการสืบทอดหลายคลาสจากคลาส Java ไม่รองรับการสืบทอดหลายคลาสเพราะอาจทำให้เกิดปัญหาไดมอนด์ได้ และแทนที่จะมองหาวิธีแก้ปัญหานี้ มีตัวเลือกที่ดีกว่าว่าเราจะบรรลุผลเดียวกันได้อย่างไร เช่น การสืบทอดหลายรายการ

ปัญหาเพชร

เพื่อให้เข้าใจปัญหาเพชรได้ง่ายขึ้น สมมติว่า Java รองรับการสืบทอดหลายรายการ ในกรณีนี้ เราสามารถมีลำดับชั้นของคลาสได้ดังที่แสดงในภาพด้านล่าง การสืบทอดหลายรายการใน Java  องค์ประกอบเทียบกับมรดก - 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()เรียกเมธอดdoSomething()superclass สิ่งนี้นำไปสู่ความกำกวมเนื่องจากคอมไพลเลอร์ไม่รู้ว่าจะใช้วิธีซูเปอร์คลาสใดในการดำเนินการ นี่คือแผนภาพคลาสรูปทรงเพชรที่เรียกว่าปัญหาเพชร นี่คือเหตุผลหลักว่าทำไม Java ไม่รองรับการสืบทอดหลายรายการ โปรดทราบว่าปัญหาข้างต้นที่มีการสืบทอดหลายคลาสสามารถเกิดขึ้นได้เฉพาะกับสามคลาสที่มีวิธีการทั่วไปอย่างน้อยหนึ่งวิธี

การสืบทอดหลายอินเทอร์เฟซ

ใน Java คลาสไม่รองรับการสืบทอดหลายรายการ แต่ได้รับการรองรับในอินเทอร์เฟซ และอินเทอร์เฟซเดียวสามารถขยายอินเทอร์เฟซอื่นๆ ได้อีกมากมาย ด้านล่างนี้เป็นตัวอย่างง่ายๆ
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();
	}
}

องค์ประกอบเทียบกับมรดก

หนึ่งในแนวทางปฏิบัติในการเขียนโปรแกรม Java ที่ดีที่สุดคือการ “อนุมัติองค์ประกอบก่อนการสืบทอด” เราจะสำรวจบางแง่มุมที่สนับสนุนแนวทางนี้
  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 ใด ๆ มันจะแจ้งให้คุณเปลี่ยนประเภทการส่งคืนในซูเปอร์คลาสหรือคลาสย่อย

    ทีนี้ ลองจินตนาการถึงสถานการณ์ที่เรามีลำดับชั้นการสืบทอดคลาสหลายระดับ และไม่สามารถเข้าถึงซูเปอร์คลาสได้ เราจะไม่มีทางเลือกอื่นนอกจากเปลี่ยนลายเซ็นเมธอดคลาสย่อยของเราหรือชื่อเพื่อลบข้อผิดพลาดในการคอมไพล์ เราจะต้องเปลี่ยนเมธอดคลาสย่อยในทุกที่ที่ถูกเรียก ดังนั้นการสืบทอดทำให้โค้ดของเราเปราะ

    ปัญหาข้างต้นจะไม่เกิดขึ้นกับการจัดองค์ประกอบ และทำให้การสืบทอดมีความน่าสนใจยิ่งขึ้น

  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. การทดสอบหน่วยทำได้ง่ายกว่ากับการจัดองค์ประกอบเพราะเรารู้ว่าเรากำลังใช้วิธีการทั้งหมดจากซูเปอร์คลาสและสามารถคัดลอกวิธีการเหล่านั้นสำหรับการทดสอบได้ ในขณะที่การสืบทอดนั้นเราพึ่งพาซูเปอร์คลาสมากกว่าและไม่ทราบวิธีการทั้งหมดของซูเปอร์คลาสที่จะใช้ ดังนั้นเราจึงต้องทดสอบวิธีการทั้งหมดของซูเปอร์คลาส ซึ่งเป็นงานพิเศษเนื่องจากการสืบทอด

    ตามหลักการแล้ว เราควรใช้การสืบทอดเมื่อความสัมพันธ์ของคลาสย่อยถึงซูเปอร์คลาสถูกกำหนดเป็น "เป็น" เท่านั้น ในกรณีอื่นทั้งหมด ขอแนะนำให้ใช้องค์ประกอบนี้

ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION