JavaRush /Blog Java /Random-MS /Pewarisan berbilang di Jawa. Perbandingan komposisi dan w...
HonyaSaar
Tahap
Москва

Pewarisan berbilang di Jawa. Perbandingan komposisi dan warisan

Diterbitkan dalam kumpulan
Beberapa ketika dahulu saya menulis beberapa catatan tentang warisan, antara muka dan komposisi dalam Java. Dalam artikel ini, kita akan melihat berbilang warisan dan kemudian mempelajari tentang faedah gubahan berbanding warisan.
Pewarisan berbilang di Jawa.  Perbandingan komposisi dan warisan - 1

Pewarisan Berbilang di Jawa

Warisan berbilang ialah keupayaan untuk mencipta kelas dengan berbilang kelas induk. Tidak seperti bahasa berorientasikan objek popular lain seperti C++, Java tidak menyokong warisan berbilang kelas. Dia tidak menyokongnya kerana kemungkinan menghadapi "masalah berlian" dan sebaliknya lebih suka menyediakan beberapa jenis pendekatan komprehensif untuk menyelesaikannya, menggunakan pilihan terbaik yang boleh kita capai hasil pewarisan yang serupa.

"Masalah Berlian"

Untuk memahami masalah berlian dengan lebih mudah, mari kita anggap bahawa warisan berbilang disokong di Jawa. Dalam kes ini, kita boleh mendapatkan kelas dengan hierarki yang ditunjukkan dalam rajah di bawah. hierarki kelas berlianMari kita anggap itu SuperClassialah kelas abstrak yang menerangkan kaedah tertentu, dan kelas ClassAdan ClassBkelas sebenar. 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(){
    }
}
Sekarang, mari kita anggap bahawa kelas ClassCmewarisi dari ClassAdan ClassBpada masa yang sama, dan pada masa yang sama mempunyai pelaksanaan berikut:
package com.journaldev.inheritance;
public class ClassC extends ClassA, ClassB{
    public void test(){
        //вызов метода родительского класса
        doSomething();
    }
}
Ambil perhatian bahawa kaedah test()memanggil kaedah doSomething()kelas induk, yang akan membawa kepada kekaburan kerana pengkompil tidak tahu kaedah superclass yang harus dipanggil. Oleh kerana bentuk rajah warisan kelas dalam situasi ini, yang menyerupai garis besar berlian segi, masalah itu dipanggil "Masalah Berlian". Inilah sebab utama mengapa Java tidak menyokong warisan kelas berbilang. Ambil perhatian bahawa masalah dengan warisan kelas berbilang juga boleh berlaku dengan tiga kelas yang mempunyai sekurang-kurangnya satu kaedah biasa.

Pewarisan berbilang dan antara muka

Anda mungkin perasan bahawa saya selalu mengatakan "warisan berbilang tidak disokong antara kelas", tetapi ia disokong antara antara muka. Contoh mudah ditunjukkan di bawah: InterfaceA.java
package com.journaldev.inheritance;
public interface InterfaceA {

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

public interface InterfaceB {

    public void doSomething();
}
Perhatikan bahawa kedua-dua antara muka mempunyai kaedah dengan nama yang sama. Sekarang katakan kita mempunyai antara muka yang mewarisi daripada kedua-dua antara muka. InterfaceC.java
package com.journaldev.inheritance;

public interface InterfaceC extends InterfaceA, InterfaceB {

    //метод, с тем же названием описан в  InterfaceA и InterfaceB
    public void doSomething();
Di sini, segala-galanya adalah ideal, kerana antara muka hanyalah tempahan/penerangan kaedah, dan pelaksanaan kaedah itu sendiri akan berada dalam kelas konkrit yang melaksanakan antara muka ini, jadi tidak ada kemungkinan menghadapi kekaburan dengan pelbagai warisan antara muka. Inilah sebabnya mengapa kelas di Java boleh mewarisi daripada berbilang antara muka. Mari tunjukkan dengan contoh di bawah. 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();
    }
}
Anda mungkin perasan bahawa setiap kali saya mengatasi kaedah yang diterangkan dalam superclass atau dalam antara muka, saya menggunakan anotasi @Override. Ini adalah salah satu daripada tiga anotasi Java terbina dalam dan anda harus sentiasa menggunakannya apabila mengatasi kaedah.

Komposisi sebagai keselamatan

Jadi bagaimana jika kita mahu menggunakan methodA()kelas ClassAdan fungsi methodB()kelas ClassBdalam ClassС? Penyelesaian untuk ini boleh menjadi komposisi - versi yang ditulis semula ClassCyang melaksanakan kedua-dua kaedah kelas ClassAdan ClassBjuga mempunyai pelaksanaan doSomething()untuk salah satu objek. 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();
    }
}

Komposisi atau warisan?

Amalan pengaturcaraan Java yang baik adalah untuk memanfaatkan komposisi berbanding warisan. Kami akan melihat beberapa aspek yang memihak kepada pendekatan ini.
  1. Katakan kita mempunyai gabungan kelas ibu bapa-waris berikut:

    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;
        }
    }

    Kod di atas menyusun dan berfungsi dengan baik, tetapi bagaimana jika ClassCia dilaksanakan secara berbeza:

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

    Ambil perhatian bahawa kaedah itu test()sudah wujud dalam kelas keturunan, tetapi mengembalikan hasil daripada jenis yang berbeza. Sekarang ClassD, sekiranya anda menggunakan IDE, ia tidak akan disusun. Anda akan dinasihatkan untuk menukar jenis pulangan dalam keturunan atau superclass.

    Sekarang mari kita bayangkan situasi di mana terdapat warisan kelas berbilang peringkat dan kelas super tidak tersedia untuk perubahan kita. Sekarang, untuk menghapuskan ralat penyusunan, kami tidak mempunyai pilihan lain selain menukar tandatangan atau nama kaedah subkelas. Kami juga perlu membuat perubahan pada semua tempat di mana kaedah ini dipanggil. Oleh itu, warisan menjadikan kod kami rapuh.

    Masalah yang diterangkan di atas tidak pernah berlaku dalam kes komposisi, dan oleh itu menjadikan yang kedua lebih disukai daripada warisan.

  2. Masalah seterusnya dengan warisan ialah kami mendedahkan semua kaedah ibu bapa kepada pelanggan. Dan jika superclass tidak direka dengan betul dan mengandungi lubang keselamatan. Kemudian, walaupun kami menjaga keselamatan sepenuhnya dalam pelaksanaan subkelas kami, kami masih bergantung pada pelaksanaan kelas induk yang cacat.

    Komposisi membantu kami menyediakan akses terkawal kepada kaedah kelas super, manakala warisan tidak mengekalkan sebarang kawalan ke atas kaedahnya. Ini juga merupakan salah satu kelebihan utama komposisi berbanding warisan.

  3. Satu lagi faedah komposisi ialah ia menambah fleksibiliti semasa memanggil kaedah. Pelaksanaan kelas ClassCyang diterangkan di atas tidak optimum dan menggunakan pengikatan awal kepada kaedah yang dipanggil. Perubahan minimum akan membolehkan kami membuat panggilan kaedah fleksibel dan membenarkan pengikatan lewat (mengikat pada masa jalan).

    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();
        }
    }

    Program di atas akan memaparkan:

    doSomething implementation of A
    doSomething implementation of B

    Fleksibiliti dalam panggilan kaedah ini tidak dilihat dengan warisan, yang menjadikan komposisi pendekatan terbaik.

  4. Ujian unit adalah lebih mudah dalam kes komposisi kerana kita tahu bahawa untuk semua kaedah yang digunakan dalam superclass kita boleh stub ujian, manakala dalam warisan kita sangat bergantung pada superclass dan tidak tahu bagaimana kaedah kelas induk akan digunakan. Oleh itu, disebabkan warisan, kita perlu menguji semua kaedah superclass, yang merupakan kerja yang tidak perlu.

    Sebaik-baiknya, warisan hanya boleh digunakan apabila perhubungan " is-a " adalah benar untuk kelas induk dan anak, jika tidak, komposisi harus diutamakan.

Artikel asal
Komen
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION