Java-da çoxlu varislik
Çoxlu miras çoxlu valideyn sinifləri ilə siniflər yaratmaq qabiliyyətidir. C++ kimi digər məşhur obyekt yönümlü dillərdən fərqli olaraq, Java çox sinif irsini dəstəkləmir. O, "almaz problemi" ilə qarşılaşma ehtimalına görə bunu dəstəkləmir və əvəzində oxşar miras nəticəsinə nail ola biləcəyimiz ən yaxşı variantlardan istifadə edərək, onu həll etmək üçün bir növ kompleks yanaşma təqdim etməyə üstünlük verir."Almaz problemi"
Almaz problemini daha sadə başa düşmək üçün tutaq ki, Java-da çoxlu varislik dəstəklənir. Bu halda biz aşağıdakı şəkildə göstərilən iyerarxiya ilə siniflər əldə edə bilərik. Fərz edək ki, bu,SuperClass
müəyyən bir metodu təsvir edən mücərrəd bir sinifdir və siniflər
ClassA
və
ClassB
real siniflərdir.
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(){
}
}
İndi fərz edək ki, sinif eyni zamanda ondan
ClassC
miras alır və eyni zamanda aşağıdakı icraya malikdir:
ClassA
ClassB
package com.journaldev.inheritance;
public class ClassC extends ClassA, ClassB{
public void test(){
//вызов метода родительского класса
doSomething();
}
}
Qeyd edək ki, metod ana sinifin
test()
metodunu çağırır
doSomething()
və bu, qeyri-müəyyənliyə gətirib çıxaracaq, çünki kompilyator hansı superklass metodunun çağırılmalı olduğunu bilmir. Fasetli almazın konturuna bənzəyən bu vəziyyətdə sinif irsiyyət diaqramının formasına görə problem “Almaz problemi” adlanır. Java-nın çoxlu sinif irsini dəstəkləməməsinin əsas səbəbi budur. Qeyd edək ki, çoxsaylı sinif irsiyyəti ilə bağlı bu problem ən azı bir ümumi metodu olan üç sinifdə də baş verə bilər.
Çoxsaylı miras və interfeyslər
Ola bilsin ki, mən həmişə “siniflər arasında çoxlu varislik dəstəklənmir” dediyimi, lakin interfeyslər arasında dəstəkləndiyini görmüsünüz. Sadə bir nümunə aşağıda göstərilmişdir:InterfaceA.java
package com.journaldev.inheritance;
public interface InterfaceA {
public void doSomething();
}
InterfaceB.java
package com.journaldev.inheritance;
public interface InterfaceB {
public void doSomething();
}
Diqqət yetirin ki, hər iki interfeysdə eyni adlı metod var. İndi deyək ki, hər iki interfeysdən miras qalan bir interfeysimiz var.
InterfaceC.java
package com.journaldev.inheritance;
public interface InterfaceC extends InterfaceA, InterfaceB {
//метод, с тем же названием описан в InterfaceA и InterfaceB
public void doSomething();
Burada hər şey idealdır, çünki interfeyslər yalnız metodun rezervasiyası/təsviridir və metodun özü bu interfeysləri həyata keçirən konkret sinifdə olacaq, ona görə də interfeyslərin çoxsaylı varisliyi ilə qeyri-müəyyənliklə qarşılaşmaq imkanı yoxdur. Buna görə Java-da siniflər bir çox interfeysdən miras qala bilər. Bunu aşağıdakı nümunə ilə göstərək.
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();
}
}
Ola bilsin ki, siz hər dəfə supersinifdə və ya interfeysdə təsvir edilən metodu ləğv edəndə @Override annotasiyasından istifadə etdiyimi görmüsünüz. Bu, üç daxili Java annotasiyasından biridir və siz üsulları ləğv edərkən həmişə ondan istifadə etməlisiniz.
Qurtuluş kimi kompozisiya
methodA()
Beləliklə, biz sinif
ClassA
və
methodB()
sinif
ClassB
funksiyasından istifadə etmək istəsək nə olacaq
ClassС
? Bunun həlli kompozisiya ola bilər -
ClassC
həm sinif metodlarını tətbiq edən
ClassA
, həm də obyektlərdən biri üçün
ClassB
tətbiqi olan yenidən yazılmış versiya.
doSomething()
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();
}
}
Tərkib, yoxsa miras?
Varislik üzərində kompozisiyadan istifadə etmək yaxşı Java proqramlaşdırma təcrübəsidir. Bu yanaşmanın lehinə bəzi aspektlərə baxacağıq.-
Tutaq ki, valideyn-varis siniflərinin aşağıdakı birləşməsinə sahibik:
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; } }
Yuxarıdakı kod tərtib edir və yaxşı işləyir, lakin
ClassC
fərqli şəkildə həyata keçirilsə nə olacaq:package com.journaldev.inheritance; public class ClassC{ public void methodC(){ } public void test(){ } }
Nəzərə alın ki, metod
test()
artıq nəsil sinifində mövcuddur, lakin fərqli bir növün nəticəsini qaytarır. İndiClassD
bir IDE istifadə edirsinizsə, o, tərtib etməyəcək. Sizə nəsil və ya super sinifdə qaytarma növünü dəyişdirməyiniz tövsiyə olunacaq.İndi siniflərin çoxsəviyyəli irsiyyətinin olduğu və dəyişikliklərimiz üçün superklassın mövcud olmadığı bir vəziyyəti təsəvvür edək. İndi kompilyasiya xətasından xilas olmaq üçün alt sinif metodunun imzasını və ya adını dəyişməkdən başqa seçimimiz yoxdur. Bu metodun çağırıldığı bütün yerlərdə də dəyişiklik etməli olacağıq. Beləliklə, miras kodumuzu kövrək edir.
Yuxarıda təsvir edilən problem kompozisiya vəziyyətində heç vaxt baş vermir və buna görə də ikincini mirasdan üstün edir.
-
Vərəsəliklə bağlı növbəti problem odur ki, biz valideynin bütün üsullarını müştəriyə təqdim edirik. Və əgər super sinif çox düzgün tərtib olunmayıbsa və təhlükəsizlik dəlikləri varsa. Sonra, alt sinifimizin həyata keçirilməsində təhlükəsizliyin tam qayğısına qalsaq da, yenə də ana sinifin qüsurlu tətbiqindən asılı olacağıq.
Kompozisiya bizə supersinifin metodlarına nəzarət edilən girişi təmin etməkdə kömək edir, halbuki miras onun metodları üzərində heç bir nəzarəti saxlamır. Bu həm də kompozisiyanın varislik üzərində əsas üstünlüklərindən biridir.
-
Kompozisiyanın başqa bir üstünlüyü ondan ibarətdir ki, metodları çağırarkən çeviklik əlavə edir. Yuxarıda təsvir edilən sinfin həyata keçirilməsi
ClassC
optimal deyil və çağırılan metoda erkən bağlanmadan istifadə edir. Minimal dəyişikliklər bizə metod çağırışını çevik etməyə və gec bağlanmağa imkan verəcək (iş zamanı bağlama).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(); } }
Yuxarıdakı proqram göstərəcək:
doSomething implementation of A doSomething implementation of B
Metod çağırışında bu çeviklik mirasla görünmür, bu da kompozisiyanı ən yaxşı yanaşma edir.
-
Kompozisiya vəziyyətində vahid sınağı daha asandır, çünki bilirik ki, super sinifdə istifadə olunan bütün üsullar üçün testləri kəsə bilərik, irsiyyətdə isə biz superklassdan çox asılıdır və ana sinfin metodlarının necə işlədiyini bilmirik. istifadə olunacaq. Deməli, irsiyyətə görə biz superklassın bütün üsullarını sınaqdan keçirməli olacağıq ki, bu da lazımsız işdir.
İdeal olaraq, miras yalnız “ is-a ” əlaqəsi valideyn və uşaq sinifləri üçün doğru olduqda istifadə edilməlidir , əks halda kompozisiyaya üstünlük verilməlidir.
GO TO FULL VERSION