1. Giriş
Abstrakt class — bu elə bir class-dır ki, onu birbaşa new ilə yaratmaq olmur. O, əsasən başqa, daha konkret class-ların əsası kimi istifadə olunur. Bu bir növ şablon və ya "çertyoj"dur, hansı ki, törəmə class-lar üçün ümumi struktur və davranışı müəyyən edir, amma konkret implementasiya detallarını onlara saxlayır.
Bax belə bir analogiya: təsəvvür elə ki, səndə "Nəqliyyat" anlayışı var. Real həyatda heç kim abstrakt "Nəqliyyat"da gəzmir (ancaq öz təsəvvüründə). Kimisi maşında gedir, kimisi gəmidə üzür, kimisi təyyarədə uçur. Abstrakt class — sənin "Nəqliyyat" çertyojundur, hansı ki, hər bir nəqliyyatın əsas xüsusiyyəti hərəkət etmək olmalıdır deyir (Move), amma necə hərəkət edəcəyi artıq child class-larda müəyyən olunur.
// Abstrakt class nümunəsi
public abstract class Transport
{
public string Model { get; set; }
// Abstrakt metod — implementasiyası yoxdur
public abstract void Move();
}
Əgər Transport tipində obyekt yaratmağa çalışsan, kompilyator səhv verəcək! Sına — və kompilyator dərhal deyəcək ki, "abstrakt class-ın nümunəsini yaratmaq olmaz".
Abstrakt class-lar nə vaxt və niyə lazımdır?
- Fərqli, amma oxşar obyektlərin ümumi xüsusiyyətlərini və davranışını bir yerdə toplamaq istəyirsən.
- "Birgə müqavilə" vermək lazımdır: məsələn, "bütün törəmələr hərəkət edə bilməlidir".
- Bəzi metodların ümumi implementasiyası var, amma bir hissə məntiq child class-larda dəqiqləşməlidir.
Abstrakt class — böyük sistem arxitekturası dizaynı üçün əla alətdir, harada ki, hamı üçün ümumi "fitchalar" var, amma spesifikası hər bir konkret child tərəfindən implementasiya olunmalıdır.
2. Abstrakt class-ın adi class-dan fərqi
Burada tez-tez suallar yaranır, ona görə gəlin hər şeyi aydın şəkildə izah edək!
Abstrakt class vs Adi class
- Adı class new ilə yaradıla bilər, bütün metodları implementasiya olunmuş ola bilər.
- Abstrakt class:
- birbaşa yaratmaq olmaz (abstract class elanında)
- implementasiyası olan və olmayan (abstrakt) metodlar ola bilər
- field-lar, property-lər, konstruktorlar və hətta static metodlar ola bilər
public abstract class Animal
{
public string Name { get; set; }
public void Sleep()
{
Console.WriteLine("Heyvan yatır!");
}
public abstract void MakeSound();
}
3. Abstrakt metodlar və property-lər
Abstrakt metod
Abstrakt metod — abstrakt class-da elan olunur, amma bədəni (implementasiyası) yoxdur. Törəmə class onu özünə uyğun implementasiya etməlidir.
public abstract class Animal
{
public string Name { get; set; }
// Abstrakt metod
public abstract void MakeSound();
}
Abstrakt property
Abstrakt property də elan etmək olar ki, bütün child-lar onu mütləq implementasiya etsin:
public abstract class Shape
{
// Abstrakt property
public abstract double Area { get; }
}
4. Abstrakt class-lardan irsi almaq
Abstrakt class həm abstrakt, həm də implementasiya olunmuş metodlar saxlaya bilər. Törəmə class bütün abstrakt metod və property-ləri implementasiya etməlidir, yoxsa özü də abstrakt olmalıdır.
Gəlin əvvəlki dərslərdə başladığımız "heyvan app"ımızı inkişaf etdirək:
public abstract class Animal
{
public string Name { get; set; }
public void Sleep()
{
Console.WriteLine($"{Name} şirin yatır!");
}
public abstract void MakeSound();
}
İndi isə adi, abstrakt olmayan class yaradaq:
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine($"{Name} deyir: Hav!");
}
}
MakeSound() implementasiya etməsən — kompilyator səhv verəcək.
Sxem: abstrakt class ilə class-ların iyerarxiyası
+---------------------+
| Animal (abstrakt) |
+---------------------+
| Name: string |
| + Sleep() |
| + MakeSound()* |
+---------------------+
^
|
+------+-------+
| |
Dog Cat
* MakeSound() — abstrakt metod
5. Polimorfizm iş başında: abstraksiya praktikada necə işləyir
Gəlin bütün bunları mini-app-ımızda tətbiq edək. Təsəvvür elə ki, heyvanlar siyahımız var — məsələn, itlər və pişiklər.
List<Animal> animals = new List<Animal>
{
new Dog { Name = "Şarik" },
new Cat { Name = "Barsik" }
};
foreach (var animal in animals)
{
animal.MakeSound(); // Polimorf çağırış: hərəsinin öz metodu işləyəcək
animal.Sleep(); // Baza class-da implementasiya olunmuş metod çağırılır
}
Cat class-ını da oxşar şəkildə implementasiya edirik:
public class Cat : Animal
{
public override void MakeSound()
{
Console.WriteLine($"{Name} deyir: Miyau!");
}
}
Real layihələrdə praktik fayda
Abstrakt class-lar böyük app arxitekturasında geniş istifadə olunur. Məsələn, qrafik redaktorlarda adətən Shape adlı abstrakt class olur, hansı ki, Draw() kimi metodlar və Area kimi property-lər saxlayır. Konkret fiqurlar (düzbucaqlı, dairə) bu abstrakt metodları öz formulaları ilə implementasiya edir. Bu yanaşma sayəsində bir çox fərqli fiqur yaratmaq olur, proqramın bütün məntiqini yenidən yazmadan.
6. Abstrakt class-ların xüsusiyyətləri və məhdudiyyətləri
Nümunə yaratmaq olmaz
Kompilyator qəti şəkildə belə etməyə icazə verməyəcək:
Animal a = new Animal(); // Səhv!
Yalnız törəmə (konkret) class-ları yaratmaq olar.
Abstrakt class-lar aşağıdakıları saxlaya bilər:
- Abstrakt metod elanları (implementasiyasız)
- Implementasiya olunmuş metodlar (bədəni var)
- Property və field-lar (abstrakt və ya implementasiya olunmuş)
- Konstruktorlar — bəli, abstrakt class-da konstruktor ola bilər, amma onu yalnız child çağırır, birbaşa obyekt yaradan kod yox.
Abstrakt class-lar irsi dəstəkləyir
Bütün bir abstraksiya iyerarxiyası qurmaq olar:
public abstract class Creature
{
public abstract void Live();
}
public abstract class Animal : Creature
{
public abstract override void Live();
public abstract void MakeSound();
}
Əgər irsi zəncirində class bütün abstrakt metodları implementasiya etmirsə, özü də abstrakt olmalıdır!
7. Abstrakt class-lar və konstruktorlar
Abstrakt class konstruktor saxlaya bilər, hansı ki, child class-ların konstruktorundan çağırılır.
Vacib xüsusiyyət: abstrakt class-ların konstruktorları adətən protected elan olunur, çünki onlar yalnız child-lar üçün nəzərdə tutulub, birbaşa obyekt yaratmaq üçün yox.
public abstract class Animal
{
public string Name { get; set; }
// Konstruktor protected elan olunub
protected Animal(string name)
{
Name = name;
Console.WriteLine($"Adı {name} olan heyvan yaradıldı");
}
public abstract void MakeSound();
}
public class Dog : Animal
{
public Dog(string name) : base(name)
{
// base(name) Animal konstruktorunu çağırır
Console.WriteLine("İt yaradıldı");
}
public override void MakeSound()
{
Console.WriteLine($"{Name} deyir: Hav!");
}
}
Niyə protected konstruktorlardan istifadə etməli?
- Məqsədin aydınlığı: protected açıq göstərir ki, konstruktor yalnız child-lar üçün nəzərdə tutulub
- Səhvlərin qarşısını almaq: kompilyator onsuz da new ilə yaratmağa icazə vermir, amma protected konstruktor bu niyyəti class API səviyyəsində də aydın edir, hətta nəzəri anlaşılmazlığı aradan qaldırır
- Yaxşı praktika: encapsulation və aydın API prinsiplərinə riayət etmək
Abstrakt class-larda public konstruktorlar da istifadə etmək olar, amma protected onların təyinatını daha dəqiq göstərir.
8. Abstrakt və virtual metodların müqayisəsi
Abstrakt metodlar — virtual metodların bir növüdür (əslində həmişə virtual olurlar, baxmayaraq ki, bu söz onlara tətbiq olunmur), amma onlarda implementasiya yazmaq olmaz. Əgər istəyirsən ki, bəzi metodları child dəyişə bilsin (amma məcbur deyil), virtual istifadə et. Əgər mütləq dəyişməlidir — yalnız abstract.
public abstract class Worker
{
// Hər kəs implementasiya etməlidir
public abstract void Work();
// İstəyən dəyişə bilər, amma məcbur deyil
public virtual void Rest()
{
Console.WriteLine("Standart istirahət!");
}
}
| Abstrakt metod | Virtual metod | |
|---|---|---|
| Baza class-da implementasiya var? | Yox, yalnız signatura (bədənin özü yoxdur) | Bəli, default implementasiya var |
| Harada elan etmək olar? | Yalnız abstrakt class-da | Hər hansı sealed olmayan class-da, o cümlədən abstrakt class-da |
| Child mütləq override etməlidir? | Mütləq, ilk abstrakt olmayan child üçün | Məcbur deyil. Override edə bilər, ya da baza implementasiyasını istifadə edə bilər |
| Birbaşa çağırmaq olar? | Yox (bədənin özü yoxdur) | Bəli (baza versiyası işləyəcək) |
| Əsas məqsəd | Child-ları öz implementasiyasını verməyə məcbur etmək. "Müqavilə" müəyyən etmək | Child-lara davranışı dəyişmək imkanı vermək, əgər ehtiyac varsa |
9. Abstrakt class-ların layihəndə tətbiqi
Tədris app-ımızı inkişaf etdiririk. İndi fərqli tip formalar üçün istifadəçi inputunu işləyən abstrakt class lazımdır.
public abstract class InputForm
{
public string Title { get; set; }
// Abstrakt metod, input prosesini implementasiya edir
public abstract void ShowAndHandleInput();
}
public class LoginForm : InputForm
{
public override void ShowAndHandleInput()
{
Console.WriteLine($"=== {Title} ===");
Console.Write("Login daxil edin: ");
string login = Console.ReadLine();
Console.Write("Şifrə daxil edin: ");
string password = Console.ReadLine();
Console.WriteLine("Avtorizasiya tamamlandı!");
}
}
public class RegistrationForm : InputForm
{
public override void ShowAndHandleInput()
{
Console.WriteLine($"=== {Title} ===");
Console.Write("Login fikirləşin: ");
string login = Console.ReadLine();
Console.Write("Şifrə fikirləşin: ");
string password = Console.ReadLine();
Console.WriteLine("Qeydiyyat tamamlandı!");
}
}
İstifadə:
InputForm[] forms = new InputForm[]
{
new LoginForm { Title = "Giriş" },
new RegistrationForm { Title = "Qeydiyyat" }
};
foreach (var form in forms)
{
form.ShowAndHandleInput();
}
Bu yanaşma proqramın böyüdükcə daha da güclü olur. Abstrakt class — genişlənən sistemlər üçün əla bazadır, burada DRY və SOLID prinsipləri (özünü təkrarlama, modul və genişlənən kod yazmaq) — sənin ən yaxşı dostlarındır.
10. Abstrakt class və metodlarla işləyərkən tipik səhvlər
Səhv №1: abstrakt metodun implementasiyası unudulub.
Əgər child class bütün abstrakt metod və property-ləri implementasiya etmirsə, kompilyator səhv verəcək. Bu tez-tez olur, xüsusən başqasının kodu və ya böyük iyerarxiyalarla işləyəndə. Diqqətlə yoxla ki, vacib bir şeyi buraxmamısan.
Səhv №2: metodu eyni anda abstract və static etmək cəhdi.
Belə etmək mümkün deyil. Abstrakt metodlar class nümunələrində implementasiya üçün nəzərdə tutulub, static isə ümumiyyətlə nümunə tələb etmir. Bu iki yanaşma bir-birinə ziddir və kompilyator buna icazə verməyəcək.
Səhv №3: "tənbəl" şəkildə bütün abstrakt üzvləri implementasiya etməmək.
Əgər bütün abstrakt metodları implementasiya etmək istəmirsənsə, class-ı da abstract etməlisən. Bu özü səhv deyil, amma bəzən qarışıq arxitekturanın əlaməti ola bilər. Bəzən qismən implementasiyanı aralıq abstrakt class-a çıxarmaq məntiqlidir ki, child-ların işi asanlaşsın.
GO TO FULL VERSION