CodeGym /Kurslar /C# SELF /Polimorfizmin praktikada istifadəsi

Polimorfizmin praktikada istifadəsi

C# SELF
Səviyyə, Dərs
Mövcuddur

1. Polimorfizm praktikada

Proqramlaşdırmada polimorfizm — elə bil universal pult kimidir: bir dənə "Səs+" düyməsini basırsan, amma televizor, audio sistem və ya kondisioner — hər biri özünəməxsus reaksiya verir, amma interfeys eynidir! Eyni qayda ilə, fərqli tip obyektlər bir metod çağırışına müxtəlif cür reaksiya verə bilər, əgər bu metod onların ümumi əcdadında virtual kimi təyin olunubsa.

C#-da polimorfizm belə işləyir: baza sinif (və ya interface) tipində dəyişən istənilən törəmə obyektə "sahib çıxa" bilər və belə dəyişəndə virtual metodlar çağırılanda, həqiqətən obyektin özündə təyin olunan xüsusi implementasiya işləyir. Bu, dinamik dəyişən məntiqə əsaslanan arxitekturaların təməlidir.

Bunu dərslikdən başqa harada istifadə edirlər?

Əlbəttə! Demək olar ki, istənilən layihədə, harada ki, oxşar, amma fərqli obyektlərlə işləmək lazımdır — zooparkdakı heyvanlardan tutmuş, qrafik interfeys elementlərinə, event handler-lardan sənəd dövriyyəsi sistemlərinə qədər.

  • Universal alqoritmlər yaratmağa imkan verir — kod abstrakt obyektlərlə işləyir, konkret implementasiyanın detallarını düşünmür.
  • Genişlənə bilənlik təmin olunur — istəsən yüz dənə yeni "heyvan", "fiqur", "handler" əlavə et, mövcud koda toxunmağa ehtiyac yoxdur.
  • Proqram komponentlərinin bir-birindən asılılığını azaldır (bu, müsahibələr və arxitektura üçün vacib mövzulardandır!).

2. Əsas sintaksis və mexanika

Gəlin, Animal, Dog, Cat siniflərimizi xatırlayaq və təkmilləşdirək ki, polimorfizmin necə işlədiyini göstərək. Artıq "Virtual zoopark" app-ından başlamışıqsa, onu bir az da inkişaf etdirək.

Virtual metodlu baza siniflər


public class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    // Virtual metod — override etmək olar
    public virtual void MakeSound()
    {
        Console.WriteLine($"{Name} müəyyən bir səs çıxarır...");
    }
}
        
public class Dog : Animal
{
    public Dog(string name) : base(name) { }

    public override void MakeSound()
    {
        Console.WriteLine($"{Name} deyir: Hav-hav!");
    }
}
public class Cat : Animal
{
    public Cat(string name) : base(name) { }

    public override void MakeSound()
    {
        Console.WriteLine($"{Name} deyir: Miyau!");
    }
}

Polimorfizmin istifadəsi: heyvanlar kolleksiyası nümunəsi

Tutaq ki, bizdə müxtəlif heyvanlardan ibarət siyahı var — ev heyvanı da, vəhşi də — və hamısına "səs çıxart" demək istəyirik. Polimorfizmsiz tip yoxlamaları və çoxlu təkrarlanan kod yazmaq lazım olardı. Polimorfizmlə isə — hər şey rahat və qısa!


// Müxtəlif növ heyvanlardan array yaradırıq
Animal[] animals = new Animal[]
{
    new Dog("Bobik"),
    new Cat("Murka"),
    new Dog("Şarik"),
    new Cat("Barsik"),
};

// Array boyunca gedirik və hər birindən səs çıxartmasını istəyirik
foreach (var animal in animals)
{
    animal.MakeSound(); // Dog və ya Cat-dakı metod işləyəcək, Animal-dakı yox!
}
        

Nəticə:

Bobik deyir: Hav-hav!
Murka deyir: Miyau!
Şarik deyir: Hav-hav!
Barsik deyir: Miyau!

Bütün sehr budur: bir universal kod — obyektin real tipindən asılı olaraq fərqli nəticələr.

Diaqram: polimorfizm necə işləyir


Animal (baza sinif)
   /           \
Dog           Cat
        
Sinif iyerarxiyası: Animal → Dog, Cat

animal.MakeSound() çağıranda, harada ki, Animal dəyişəni əslində Dog və ya Cat ola bilər, .NET runtime özü başa düşür ki, hansı MakeSound() metodunu çağırmaq lazımdır.

3. Tipik məsələlərin polimorfizmlə həlli

Nümunə 1: Universal siyahı, fərqli hərəkətlər

Tutaq ki, oyun yazırsan. Səndə baza GameObject sinifi var, törəmələr — düşmənlər, dostlar, maneələr. Hamısı hərəkət edə bilir, hamıda Update() metodu var, amma implementasiya fərqlidir.

public class GameObject
{
    public virtual void Update() { }
}

public class Enemy : GameObject
{
    public override void Update()
    {
        Console.WriteLine("Düşmən hücum edir!");
    }
}

public class Friend : GameObject
{
    public override void Update()
    {
        Console.WriteLine("Dost kömək edir!");
    }
}

GameObject[] objects = new GameObject[]
{
    new Enemy(),
    new Friend(),
    new Enemy()
};

foreach (var obj in objects)
{
    obj.Update();
}
// Çap edəcək:
// Düşmən hücum edir!
// Dost kömək edir!
// Düşmən hücum edir!

Nümunə 2: Obyektləri metodlara ötürmək

Baza tipli parametr qəbul edə bilərsən və istənilən törəmə tipdən istifadə edə bilərsən. Bu, xüsusilə obyektə yeni törəmələr əlavə olunanda çox rahatdır.

public static void FeedAnimal(Animal animal)
{
    Console.Write($"{animal.Name}: ");
    animal.MakeSound();
    Console.WriteLine("Və yemək alır.");
}

FeedAnimal(new Dog("Rex"));
FeedAnimal(new Cat("Sima"));

// Nəticə:
// Rex: Rex deyir: Hav-hav!
// Və yemək alır.
// Sima: Sima deyir: Miyau!
// Və yemək alır.

Diqqət et: belə metodu polimorfizmsiz yazmaq çox əziyyətli olardı — heyvanın tipini ayrıca yoxlamaq, bir yığın if-else yazmaq və düzgün metodları əl ilə çağırmaq lazım gələrdi.

Vacib qeyd: runtime-da bağlanma

Polimorfizm ona görə işləyir ki, virtual metod çağırışları dinamik — yəni proqram işləyərkən baş verir. Buna gec bağlanma (late binding) deyirlər. Dəyişəni Animal kimi bilsək də, metod yenə də real obyektin özündə təyin olunan versiyanı çağıracaq.

Əgər metod virtual deyilsə, çağırış həmişə dəyişənin tipinə görə gedəcək (obyektin tipinə yox). Ona görə də, çeviklik istəyirsənsə, virtual sözündən çəkinmə!

4. Praktika: app-ımızı genişləndiririk

Tutaq ki, virtual zooparkına yeni bir funksiya əlavə etmək istədin: indi hər heyvan yalnız MakeSound() yox, həm də hərəkət etməlidir (Move()). Amma hamıda bu fərqli cür reallaşır.

1. Baza sinifə virtual metod əlavə edirik


public class Animal
{
    public string Name { get; set; }

    public Animal(string name)
    {
        Name = name;
    }

    public virtual void MakeSound()
    {
        Console.WriteLine($"{Name} müəyyən bir səs çıxarır...");
    }

    public virtual void Move()
    {
        Console.WriteLine($"{Name} qeyri-müəyyən şəkildə hərəkət edir...");
    }
}
        

2. Törəmə siniflərdə Move()-u özümüzə uyğun yazırıq

public class Dog : Animal
{
    public Dog(string name) : base(name) { }

    public override void MakeSound()
    {
        Console.WriteLine($"{Name} deyir: Hav-hav!");
    }

    public override void Move()
    {
        Console.WriteLine($"{Name} çubuğun arxasınca qaçır.");
    }
}

public class Cat : Animal
{
    public Cat(string name) : base(name) { }

    public override void MakeSound()
    {
        Console.WriteLine($"{Name} deyir: Miyau!");
    }

    public override void Move()
    {
        Console.WriteLine($"{Name} yumşaq pəncələri ilə səssizcə addımlayır.");
    }
}

3. Kolleksiyada hər iki metodu istifadə edirik

Animal[] animals = new Animal[]
{
    new Dog("Bim"),
    new Cat("Lüsyə")
};

foreach (var animal in animals)
{
    animal.MakeSound();
    animal.Move();
}

Nəticə:

Bim deyir: Hav-hav!
Bim çubuğun arxasınca qaçır.
Lüsyə deyir: Miyau!
Lüsyə yumşaq pəncələri ilə səssizcə addımlayır.

Real app-larda bu yanaşma çox güclü və təkrar istifadə oluna bilən modullar yazmağa imkan verir. Məsələn, oyunlarda bu, bütün obyekt menecerlərinin — NPC-dən effektlərə qədər — əsasını təşkil edir.

5. Praktik tapşırıq: müxtəlif fiqurlar çəkirik

Gəlin, zooparkdan yox, qrafikadan nümunə götürək. Shape adlı baza sinif və virtual Draw() metodu yaradaq, sonra onu genişləndirək.

public class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Müəyyən olunmamış fiqur çəkilir.");
    }
}

public class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Dairə çəkilir.");
    }
}

public class Rectangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Düzbucaqlı çəkilir.");
    }
}

// Fiqurlar kolleksiyası
Shape[] shapes = new Shape[]
{
    new Circle(),
    new Rectangle(),
    new Circle()
};

foreach (var shape in shapes)
{
    shape.Draw();
}

Burada Draw() Shape tipli dəyişəndə çağırılır, amma əslində Circle və ya Rectangle-dəki metodlar işləyir. Real qrafik redaktor və kitabxanalarda, məsələn, WinForms və ya WPF-də də məhz belə işləyir.

6. Ümumi yanaşma: universal alqoritmlər yazırıq

Polimorfizm kodunu çevik və genişlənə bilən alətə çevirir. Məsələn, kolleksiyada təkcə Dog və Cat yox, həm də Hamster, Parrot və başqa istənilən heyvan ola bilər — və sən onları eyni üsulla işləyə bilərsən, əlavə kod yazmadan.

Bundan əlavə, törəmə obyektləri baza tipli metodlara rahatlıqla ötürə bilərsən:

void PrintAnimalInfo(Animal animal)
{
    Console.WriteLine($"Adı: {animal.Name}");
    animal.MakeSound();
    animal.Move();
}

Animal hamster = new Animal("Xoma");
Animal dog = new Dog("Lord");
PrintAnimalInfo(hamster); // Animal-dakı metod işləyir
PrintAnimalInfo(dog);     // Dog versiyası işləyir

7. Faydalı nüanslar

Tez-tez verilən suallar və tələlər

Bir çox yeni başlayanlar düşünür ki, əgər Dog obyektini yaradıb Dog myDog kimi dəyişən elan etsələr, Animal myDog ilə fərqi olmayacaq. Əslində, dəyişəni Animal kimi elan etsən, o yalnız Animal-da olanları "görür" (override olunmuş metodlar istisna), Dog kimi elan etsən isə — hər şeyi görür: Bark() və spesifik property-lər də daxil.

Həmçinin bilmək vacibdir: əgər baza sinifdə metod virtual deyilsə, onu override etmək olmur. Əgər törəmədə belə metoda override yazmağa çalışsan — kompilyator səhv verəcək.

Yeri gəlmişkən, əgər virtual olmayan metodu həqiqətən dəyişmək istəyirsənsə, new açar sözündən istifadə et, amma bu artıq advanced (və konfliktli!) hallara aiddir.

Bunu niyə müsahibədə çox soruşurlar?

Çünki polimorfizm proqramçı üçün elə bil İsveçrə bıçağıdır: necə işlədiyini başa düşürsənsə, genişlənə bilən və rahat dəstəklənən sistemlər qura bilirsən, kodun təkcə işləməyəcək, həm də uzunömürlü olacaq. Məsələn, səndən müxtəlif ödəniş formalarının işlənməsini istəyə bilərlər (klassika: BankCard, PayPal, Bitcoin) — və gözləyirlər ki, ümumi interface (və ya abstract baza sinif) və Pay() metodu yaradacaqsan, sonra isə müştərilər Pay(BankCard), Pay(PayPal), Pay(Bitcoin) qəbul edə biləcək — və içəridə necə işlədiyini bilməyəcəklər.

8. Yeni başlayanların tipik səhvləri

Ən çox rast gəlinən səhvlərdən biri — törəmə sinifin spesifik metodunu baza tipli dəyişənlə çağırmağa cəhd etməkdir. Məsələn, belə:


Animal animal = new Dog("Tuzik");
animal.Bark(); // Səhv! Animal-da Bark metodu yoxdur.
        

Niyə işləmir? Çünki Animal tipli dəyişən yalnız Animal-da elan olunanları "görür", hətta əslində Dog olsa belə. Yalnız baza sinifdə təyin olunan və törəmədə override olunan metodları çağırmaq olar.

Əgər mütləq Dog-a məxsus metodu çağırmaq lazımdırsa — tip çevirmə etməlisən:

Animal animal = new Dog("Tuzik");
if (animal is Dog dog)
{
    dog.Bark();
}

Amma çox vaxt belə edirsənsə, deməli, arxitekturada nəsə düz deyil (və ya irsiyyətdən sui-istifadə edirsən).

2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Virtual metod ilə əsas polimorfizmin reallaşdırılması
Virtual metod ilə əsas polimorfizmin reallaşdırılması
2
Tapşırıq
C# SELF, səviyyə, dərs
Bağlanıb
Polimorf obyektlərlə işləmək üçün universal funksiya
Polimorf obyektlərlə işləmək üçün universal funksiya
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION