Java-da çoxlu varislik
Çoxlu miras çoxlu supersiniflərdən miras qalan sinif yaratmağa imkan verir. C++ kimi bəzi digər məşhur obyekt yönümlü proqramlaşdırma dillərindən fərqli olaraq, Java siniflərdən çoxlu miras almağa imkan vermir. Java çox sinif irsiyyətini dəstəkləmir, çünki bu, almaz probleminə səbəb ola bilər. Və bu problemi həll etməyin yollarını axtarmaq əvəzinə, çoxlu miras kimi eyni nəticəni əldə edə biləcəyimiz daha yaxşı variantlar var.Almaz problemi
Almaz problemini daha asan başa düşmək üçün tutaq ki, Java-da çoxlu varislik dəstəklənir. Bu halda, aşağıdakı şəkildə göstərildiyi kimi bir sinif iyerarxiyasına sahib ola bilərik. Fərz edək ki, sinifSuperClass
mücərrəddir və orada hansısa metod elan olunub. Həm konkret siniflər, ClassA
həm də 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
İndi fərz edək ki, biz həyata keçirmək və onu ClassA
və -dən miras almaq istəyirik ClassB
.
package com.journaldev.inheritance;
public class ClassC extends ClassA, ClassB{
public void test(){
//calling super class method
doSomething();
}
}
Qeyd edək ki, metod test()
superclass metodunu çağırır doSomething()
. Bu, qeyri-müəyyənliyə gətirib çıxarır, çünki kompilyator hansı superclass metodunu icra edəcəyini bilmir. Bu almaz problemi adlanan almaz formalı sinif diaqramıdır. Java-nın çoxlu varisliyi dəstəkləməməsinin əsas səbəbi budur. Qeyd edək ki, yuxarıda göstərilən çoxsaylı sinif irsi problemi yalnız ən azı bir ümumi metodu olan üç sinifdə baş verə bilər.
Çoxsaylı İnterfeys Varisliyi
Java-da siniflərdə çoxlu varislik dəstəklənmir, lakin interfeyslərdə dəstəklənir. Və bir interfeys bir çox digər interfeysləri genişləndirə bilər. Aşağıda sadə bir nümunə var.package com.journaldev.inheritance;
public interface InterfaceA {
public void doSomething();
}
package com.journaldev.inheritance;
public interface InterfaceB {
public void doSomething();
}
Qeyd edək ki, hər iki interfeys eyni metodu elan edir. İndi biz aşağıdakı nümunədə göstərildiyi kimi bu interfeyslərin hər ikisini genişləndirən interfeys yarada bilərik.
package com.journaldev.inheritance;
public interface InterfaceC extends InterfaceA, InterfaceB {
//same method is declared in InterfaceA and InterfaceB both
public void doSomething();
}
Bu əla işləyir, çünki interfeyslər yalnız metodları elan edir və tətbiq interfeysi miras alan siniflərdə həyata keçiriləcək. Beləliklə, çoxlu interfeys varisliyində qeyri-müəyyənlik əldə etmək üçün heç bir yol yoxdur.
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();
}
}
Nəzərə alın ki, hər hansı superclass metodunu ləğv etdikdə və ya interfeys metodunu həyata keçirdiyiniz zaman annotasiyadan istifadə edin @Override
. methodA()
Bir sinif funksiyasından ClassA
və methodB()
bir sinif funksiyasından ClassB
bir sinifdə istifadə etmək istəsək nə olacaq ClassC
? Həll kompozisiyanın istifadəsindədir. ClassC
Aşağıda həm sinif metodlarını, həm də doSomething()
obyektlərdən birinin metodunu müəyyən etmək üçün kompozisiyadan istifadə edən sinfin versiyası var .
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();
}
}
Tərkibi vərəsəlik
Ən yaxşı Java proqramlaşdırma təcrübələrindən biri “mirasdan əvvəl tərkibi təsdiqləməkdir”. Bu yanaşmaya üstünlük verən bəzi aspektləri araşdıracağıq.-
Deyək ki, bizim super sinifimiz və onu genişləndirən bir sinifimiz var:
package com.journaldev.inheritance; public class ClassC{ public void methodC(){ } } package com.journaldev.inheritance; public class ClassD extends ClassC{ public int test(){ return 0; } }
Yuxarıdakı kod tərtib edir və yaxşı işləyir. Ancaq sinifin tətbiqini
ClassC
aşağıda göstərildiyi kimi dəyişdirsək nə olar:package com.journaldev.inheritance; public class ClassC{ public void methodC(){ } public void test(){ } }
Qeyd edək ki, metod
test()
artıq alt sinifdə mövcuddur, lakin qaytarma növü fərqlidir. İndi sinifClassD
kompilyasiya etməyəcək və əgər siz hər hansı bir IDE istifadə etsəniz, o, sizə supersinif və ya alt sinifdə qaytarma növünü dəyişməyi təklif edəcək.İndi elə bir vəziyyəti təsəvvür edin ki, bizdə çoxsəviyyəli sinif irsiyyət iyerarxiyası var və superklassa çıxışımız yoxdur. Kompilyasiya xətasını aradan qaldırmaq üçün alt sinif metodu imzamızı və ya onun adını dəyişməkdən başqa seçimimiz olmayacaq. Həm də alt sinif metodunu çağırıldığı bütün yerlərdə dəyişdirməli olacağıq. Beləliklə, miras kodumuzu kövrək edir.
Yuxarıdakı problem kompozisiya ilə heç vaxt baş verməyəcək və bu onu miras üçün daha cəlbedici edir.
-
Vərəsəliklə bağlı başqa bir problem odur ki, biz superklassın bütün üsullarını müştəriyə təqdim edirik və əgər super sinifimiz düzgün tərtib olunmayıbsa və təhlükəsizlik boşluqları varsa, sinfimizin ən yaxşı tətbiqini həyata keçirsək də, zəif tətbiqdən təsirlənirik. super sinifdən. Kompozisiya bizə superclass metodlarına nəzarət edilən girişi təmin etməkdə kömək edir, halbuki miras superklass metodları üzərində nəzarəti təmin etmir. Bu, həm də irsiyyətdən kompozisiyanın əsas üstünlüklərindən biridir.
-
Kompozisiyanın başqa bir üstünlüyü ondan ibarətdir ki, o, çağırış üsullarında çevikliyə imkan verir. Yuxarıda təqdim etdiyimiz sinifin tətbiqi
ClassC
optimal deyil və tərtib vaxtının çağırılacaq metoda bağlı olmasını təmin edir. Minimum dəyişikliklərlə metod çağırışını çevik və dinamik edə bilərik.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(); } }
Yuxarıda göstərilən proqramın nəticəsi:
doSomething implementation of A doSomething implementation of B
Metod çağırışında bu çeviklik mirasla mövcud deyil, bu da kompozisiya seçiminə başqa bir üstünlük verir.
-
Bölmə testini kompozisiya ilə etmək daha asandır, çünki biz bilirik ki, biz super sinifdəki bütün üsullardan istifadə edirik və onları sınaq üçün kopyalaya bilərik. Halbuki mirasda biz daha çox supersinfdən asılıyıq və istifadə olunacaq supersinifin bütün üsullarını bilmirik. Beləliklə, biz irsiyyət səbəbindən əlavə iş olan superklassın bütün üsullarını sınaqdan keçirməliyik.
İdeal olaraq, biz mirasdan yalnız alt sinifdən supersinif əlaqəsi "olur" kimi müəyyən edildikdə istifadə etməliyik. Bütün digər hallarda kompozisiyadan istifadə etmək tövsiyə olunur.
GO TO FULL VERSION