1. Kolleksiyalarda polimorfizm: nə üçün lazımdır?
Gəlin bir sualla başlayaq: “Ümumiyyətlə, real proqramlarda polimorfizm nəyə lazımdır?”
Zoopark təsəvvür edin. Sizin baza sinfiniz Animal var, üstəlik bir sıra törəmələr: Dog, Cat, Cow, Parrot və hətta Platypus (ekzotika həvəskarları üçün ornitorinq). Onların hər biri səs çıxarmağı bacarır (makeSound()), amma bunu özünəməxsus şəkildə edir.
Hər heyvan üçün ayrıca massivlər yaratmaq əvəzinə, Animal tipli massiv və ya siyahı elan edir və ora istənilənini qoyursunuz:
Animal[] animals = {
new Dog(),
new Cat(),
new Cow(),
new Parrot()
};
İndi bu massiv üzrə dövr edib hər biri üçün makeSound() çağırmaq olar:
for (Animal animal : animals) {
animal.makeSound();
}
Sehr! Hər obyekt hansı səsi çıxarmalı olduğunu özü bilir və sizə çoxlu if-lər və ya switch-lər yazmaq lazım deyil.
Bənzətmə ilə nümunə
Sanki heyvanlar qrupuna “Səs!” əmri verirsiniz və hərəsi özü qərar verir: it hürür, pişik miyoldayır, inək — mökür. Kimin kim olduğunu dəqiqləşdirmirsiniz — sadəcə eyni metodu çağırırsınız.
2. Praktiki nümunə: əməkdaşlar ierarxiyası
Gəlin nümunəni həyata (və gələcək IT işinizə) yaxınlaşdıraq. Təsəvvür edin ki, müxtəlif əməkdaşları olan şirkətimiz var: menecerlər, proqramçılar, testçilər. Hər birinin work() metodu var, amma o, fərqli-fərqli icra olunur.
Baza sinfinin elan edilməsi
public class Employee {
public void work() {
System.out.println("Əməkdaş işləyir...");
}
}
Alt siniflər
public class Manager extends Employee {
@Override
public void work() {
System.out.println("Menecer iclas keçirir.");
}
}
public class Developer extends Employee {
@Override
public void work() {
System.out.println("Proqramçı kod yazır.");
}
}
public class Tester extends Employee {
@Override
public void work() {
System.out.println("Testçi xətaları axtarır.");
}
}
Baza tipli massiv/siyahının istifadəsi
public class CompanyDemo {
public static void main(String[] args) {
Employee[] team = {
new Manager(),
new Developer(),
new Tester(),
new Developer()
};
for (Employee e : team) {
e.work(); // Hər obyekt üçün metodun "doğru" versiyası çağırılacaq
}
}
}
İcra nəticəsi:
Menecer iclas keçirir.
Proqramçı kod yazır.
Testçi xətaları axtarır.
Proqramçı kod yazır.
Üstünlüklər nələrdir?
- “Əgər bu Developer-dirsə — filanı et” tipli çoxlu yoxlamalar yazmırsınız.
- Yeni əməkdaş əlavə etmək (məsələn, Designer) — sadəcə yeni sinif yaratmaq və onu massivə əlavə etməkdir.
- Əməkdaşlar massivindən istifadə edən kod ümumiyyətlə dəyişmir!
3. Polimorfizmin üstünlükləri: çeviklik və genişləndirilə bilənlik
Təsəvvür edək ki, şirkətinizdə yeni tip əməkdaş — Designer meydana çıxdı. Sizə lazım olan tək şey — yeni sinif yaratmaqdır:
public class Designer extends Employee {
@Override
public void work() {
System.out.println("Dizayner maketlər hazırlayır.");
}
}
İndi dizayneri komandaya əlavə etmək olar:
Employee[] team = {
new Manager(),
new Developer(),
new Tester(),
new Designer()
};
Vuala! Proqram dərhal yeni əməkdaş tipi ilə düzgün işləyir və massivi gəzib work() çağıran kodda bir sətir belə dəyişmir.
Bu da genişləndirilə bilənlik deməkdir: kodunuz yeni obyekt tiplərinə asanlıqla uyğunlaşır.
4. Polimorfizmin məhdudiyyətləri: medalın digər tərəfi
Təəssüf ki, hər sehrin öz məhdudiyyətləri var (və hər RPG-də olduğu kimi bunun da bir qiyməti var).
Yalnız baza sinfinin metodları əlçatandır
Employee tipli dəyişənlə işləyərkən yalnız Employee sinfində elan edilmiş metodları çağıra bilərsiniz. Əgər Developer sinfində xüsusi writeCode() metodu varsa, onu birbaşa çağıra bilməyəcəksiniz:
Employee e = new Developer();
// e.writeCode(); // Tərtib xətası: belə bir metod Employee-də yoxdur!
Çox istəyirsinizsə, spesifik metodu çağırmaq üçün tip çevirməsi etməli olacaqsınız. Amma bu, ən son çarədir. Əgər tez-tez tipləri çevirirsinizsə, bəlkə də siniflərin layihələndirilməsini yenidən gözdən keçirməyə dəyər — baza sinfi və ya interfeys lazımi metodu ehtiva etməlidir.
if (e instanceof Developer) {
Developer dev = (Developer) e;
dev.writeCode();
}
Ancaq onda bu yanaşmanın naminə qurduğumuz universallıq və zərifliyi itiririk. Buna görə baza sinfini elə layihələndirməyə çalışın ki, orada həqiqətən bütün törəmələrə lazım olan metodlar olsun.
5. Təcrübə: əməkdaşlar ierarxiyasını reallaşdıraq
Gəlin xoşu faydalı ilə birləşdirək: bir neçə tip əməkdaşın olduğu sadə tətbiq yazaq və onları emal etmək üçün polimorfizmdən istifadə edək.
Addım 1: Baza sinfi və alt siniflər
// Employee.java
public class Employee {
public void work() {
System.out.println("Əməkdaş işləyir...");
}
}
// Manager.java
public class Manager extends Employee {
@Override
public void work() {
System.out.println("Menecer iclas keçirir.");
}
}
// Developer.java
public class Developer extends Employee {
@Override
public void work() {
System.out.println("Proqramçı kod yazır.");
}
}
// Tester.java
public class Tester extends Employee {
@Override
public void work() {
System.out.println("Testçi xətaları axtarır.");
}
}
Addım 2: Əsas sinif
// CompanyDemo.java
public class CompanyDemo {
public static void main(String[] args) {
Employee[] team = {
new Manager(),
new Developer(),
new Tester(),
new Developer()
};
for (Employee e : team) {
e.work();
}
}
}
Addım 3: Genişləndirilə bilənliyi əlavə edək
Tutaq ki, bir aydan sonra şirkətdə yeni əməkdaş — dizayner peyda oldu. Lazım olan tək şey:
public class Designer extends Employee {
@Override
public void work() {
System.out.println("Dizayner maketlər hazırlayır.");
}
}
Bu qədər, indi dizayneri komandaya əlavə etmək olar:
Employee[] team = {
new Manager(),
new Developer(),
new Tester(),
new Designer()
};
Nəticə
Bütün əsas kod (CompanyDemo) dəyişməz qaldı! Bu, polimorfizmin gücüdür.
6. Polimorfizmdən istifadə zamanı tipik səhvlər
Səhv №1: Baza tipli istinad vasitəsilə spesifik metodlara çıxış gözləmək.
Çox vaxt yeni başlayanlar super sinif tipli dəyişən vasitəsilə alt sinfin spesifik metodlarını çağırmağa çalışırlar. Məsələn:
Employee e = new Developer();
// e.writeCode(); // Xəta! Belə metod Employee-də müəyyənləşdirilməyib.
Spesifik metodu çağırmaq üçün tipi çevirmək lazım gəlir, lakin onda universallıq itir.
Səhv №2: @Override annotasiyasından istifadə etməmək.
Annotasiyanı unutduqda, təsadüfən adında səhv edərək (məsələn) yeni metod yaza bilərsiniz. Bu halda polimorfizm sadəcə işləməyəcək və super sinifdəki versiya çağırılacaq.
Səhv №3: Ümumi interfeysin olmaması.
Əgər baza sinfi lazımi metodu ehtiva etmirsə, polimorfizm mümkün deyil. Məsələn, Employee-də work() metodu yoxdursa, əməkdaşlar massivi üzrə dövr həmin metodu hamı üçün çağıra bilməyəcək.
Səhv №4: Açıq/Qapalı prinsipinin pozulması.
Yeni əməkdaş tipini əlavə etmək üçün massivi/siyahını gəzən kodu dəyişməli olursunuzsa — deməli, polimorfizmdən düzgün istifadə etmirsiniz.
GO TO FULL VERSION