JavaRush /จาวาบล็อก /Random-TH /การสืบทอดหลายรายการใน Java การเปรียบเทียบองค์ประกอบและมรด...
HonyaSaar
ระดับ
Москва

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

เผยแพร่ในกลุ่ม
เมื่อไม่นานมานี้ ฉันเขียนโพสต์หลายโพสต์เกี่ยวกับการสืบทอด อินเทอร์เฟซ และองค์ประกอบใน Java ในบทความนี้ เราจะดูที่การสืบทอดหลายรายการ จากนั้นเรียนรู้เกี่ยวกับประโยชน์ของการเรียบเรียงมากกว่าการสืบทอด
การสืบทอดหลายรายการใน Java  การเปรียบเทียบองค์ประกอบและมรดก - 1

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

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

“ปัญหาเพชร”

เพื่อให้เข้าใจปัญหาเพชรได้ง่ายขึ้น สมมติว่า Java รองรับการสืบทอดหลายรายการ ในกรณีนี้เราสามารถรับคลาสที่มีลำดับชั้นดังแสดงในรูปด้านล่าง ลำดับชั้นของคลาสเพชรสมมติว่า SuperClassเป็นคลาสนามธรรมที่อธิบายวิธีการบางอย่าง และคลาส ClassAและ ClassBเป็นคลาสจริง SuperClass.java
package com.journaldev.inheritance;
public abstract class SuperClass {
   	public abstract void doSomething();
}
ClassA.java
package com.journaldev.inheritance;
public class ClassA extends SuperClass{
    @Override
 public void doSomething(){
        System.out.println("Какая-то реализация класса A");
    }
  //собственный метод класса  ClassA
    public void methodA(){
    }
}
ตอนนี้ สมมติว่าคลาส ClassCสืบทอดมาจาก ClassAและ ClassBในเวลาเดียวกัน และในเวลาเดียวกันก็มีการใช้งานดังต่อไปนี้:
package com.journaldev.inheritance;
public class ClassC extends ClassA, ClassB{
    public void test(){
        //вызов метода родительского класса
        doSomething();
    }
}
โปรดทราบว่าเมธอดนี้ test()เรียกเมธอด doSomething()ของคลาสพาเรนต์ ซึ่งจะทำให้เกิดความคลุมเครือ เนื่องจากคอมไพลเลอร์ไม่รู้ว่าควรเรียกเมธอดซูเปอร์คลาสใด เนื่องจากรูปร่างของแผนภาพการสืบทอดคลาสในสถานการณ์นี้ ซึ่งคล้ายกับโครงร่างของเพชรเหลี่ยมเพชรพลอย ปัญหาจึงเรียกว่า "ปัญหาเพชร" นี่คือเหตุผลหลักว่าทำไม Java ไม่รองรับการสืบทอดหลายคลาส โปรดทราบว่าปัญหานี้กับการสืบทอดหลายคลาสยังสามารถเกิดขึ้นได้กับสามคลาสที่มีวิธีการทั่วไปอย่างน้อยหนึ่งวิธี

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

คุณอาจสังเกตเห็นว่าฉันมักจะพูดว่า "ไม่รองรับการสืบทอดหลายรายการระหว่างคลาส" แต่ได้รับการรองรับระหว่างอินเทอร์เฟซ ตัวอย่างง่ายๆ แสดงไว้ด้านล่าง: InterfaceA.java
package com.journaldev.inheritance;
public interface InterfaceA {

    public void doSomething();
}
InterfaceB.java
package com.journaldev.inheritance;

public interface InterfaceB {

    public void doSomething();
}
โปรดสังเกตว่าอินเทอร์เฟซทั้งสองมีวิธีการที่มีชื่อเหมือนกัน ตอนนี้ สมมติว่าเรามีอินเทอร์เฟซที่สืบทอดมาจากอินเทอร์เฟซทั้งสอง InterfaceC.java
package com.journaldev.inheritance;

public interface InterfaceC extends InterfaceA, InterfaceB {

    //метод, с тем же названием описан в  InterfaceA и InterfaceB
    public void doSomething();
ที่นี่ ทุกอย่างเหมาะอย่างยิ่ง เนื่องจากอินเทอร์เฟซเป็นเพียงการจอง/คำอธิบายของวิธีการ และการนำวิธีการไปใช้งานนั้นจะอยู่ในคลาสที่เป็นรูปธรรมที่ใช้อินเทอร์เฟซเหล่านี้ ดังนั้นจึงไม่มีความเป็นไปได้ที่จะเผชิญกับความกำกวมจากการสืบทอดอินเทอร์เฟซหลายแบบ นี่คือสาเหตุที่คลาสใน Java สามารถสืบทอดจากหลายอินเทอร์เฟซได้ มาแสดงด้วยตัวอย่างด้านล่าง InterfacesImpl.java
package com.journaldev.inheritance;

public class InterfacesImpl implements InterfaceA, InterfaceB, InterfaceC {

    @Override
    public void doSomething() {
        System.out.println("doSomething реализация реального класса ");
    }

    public static void main(String[] args) {
        InterfaceA objA = new InterfacesImpl();
        InterfaceB objB = new InterfacesImpl();
        InterfaceC objC = new InterfacesImpl();

        //все вызываемые ниже методы получат одинаковую реализацию конкретного класса

        objA.doSomething();
        objB.doSomething();
        objC.doSomething();
    }
}
Возможно, вы обратor внимание, что я каждый раз, когда я переопределяю метод описанный в суперклассе or в интерфейсе я использую аннотацию @Override. Это одна из трех встроенных Java-аннотаций и вам следует всегда использовать ее при переопределении методов.

Композиция How спасение

Так что же делать, если мы хотим использовать функцию methodA() класса ClassA и methodB() класса ClassB в ClassС? Решением этого может стать композиция – переписанная version ClassC реализующая оба метода классов ClassA и ClassB, также имеющая реализацию doSomething() для одного из an objectов. ClassC.java
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();
    }
}

Композиция or наследование?

Хорошим тоном программирования на Java является – использовать преимущества композиции перед наследованием. Мы рассмотрим некоторые аспекты в пользу такого подхода.
  1. Предположим, мы имеем следующую связку классов родитель-наследник:

    ClassC.java

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

    ClassD.java

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

    Код выше прекрасно компorруется и работает, но что, если ClassC будет реализован иначе:

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

    Отметьте, что метод test() уже существует в классе наследнике, но возвращает результат другого типа. Теперь ClassD, в случае если вы используете IDE, не будет скомпorрован. Вам будет рекомендовано изменить тип возвращаемого результата в классе наследнике or в суперклассе.

    Теперь представим себе такую ситуацию, где имеется многоуровневое наследование классов и суперкласс не доступен для наших изменений. Теперь, чтобы избавиться от ошибки компиляции, у нас нет других вариантов, кроме How изменить сигнатуру or Name метода подкласса. Также нам придется внести изменения во все места, где этот метод вызывался. Таким образом, наследование делает наш code хрупким.

    Проблема, описанная выше, никогда не встречается в случае композиции, и поэтому делает последнюю более предпочтительной перед наследованием.

  2. Следующая проблема с наследованием заключается в том, что мы демонстрируем все методы родителя клиенту. И если суперкласс разработан не очень корректно и содержит дыры в «безопасности». Тогда, несмотря на то, что мы fully позаботимся о безопасности при реализации нашего подкласса, мы все равно будем зависеть от ущербной реализации класса-родителя.

    Композиция помогает нам в обеспечении контролируемого доступа к методам суперкласса, тогда How наследование не поддерживает ниHowого контроля над его методами. Это - также одно из главных преимуществ композиции над наследованием.

  3. Еще одно преимущество композиции то, что оно добавляет гибкости при вызове методов. Реализация класса ClassC, описанная выше, не оптимальна и использует раннее связывание с вызываемым методом. Минимальные изменения позволят нам сделать вызов методов гибким и обеспечить позднее связывание (связывание во время выполнения).

    ClassC.java

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

    ตามหลักการแล้ว ควรใช้การสืบทอดเฉพาะเมื่อความสัมพันธ์ " is-a " เป็นจริงสำหรับคลาสพาเรนต์และคลาสรอง ไม่เช่นนั้นควรใช้องค์ประกอบมากกว่า

บทความต้นฉบับ
ความคิดเห็น
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION