Menu
Coddy logo textTech

Polimorfik İterasyon

Coddy'nin C Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 42 / 61.

Arayüzler oluşturmayı ve bunları farklı somut fonksiyonlarla uygulamayı öğrendiniz. Asıl kazanç, farklı nesnelerden oluşan bir koleksiyona tek tip bir şekilde davranabildiğinizde — içlerinde hangi özel davranışın tanımlandığına bakılmaksızın, her birinin üzerinde dönerek aynı fonksiyon işaretçisini çağırdığınızda — ortaya çıkar.

Her birine kendi execute fonksiyonu atanmış Action struct'larından oluşan bir diziniz olduğunu hayal edin:

typedef void (*ExecuteFunc)(void);

typedef struct {
    const char* name;
    ExecuteFunc execute;
} Action;

void jump(void) { printf("Jumping!\n"); }
void run(void)  { printf("Running!\n"); }
void rest(void) { printf("Resting...\n"); }

Bu eylemlerden oluşan bir dizi oluşturabilir, her birini farklı bir fonksiyona bağlayabilir ve ardından basit bir for döngüsü ile bunlar üzerinde dönebilirsiniz:

int main() {
    Action actions[3] = {
        { "Jump", jump },
        { "Run", run },
        { "Rest", rest }
    };
    
    for (int i = 0; i < 3; i++) {
        printf("%s: ", actions[i].name);
        actions[i].execute();
    }
    return 0;
}

Döngü, her bir eylemin dahili olarak ne yaptığını bilmez veya umursamaz. Sadece her bir öğe üzerinde execute() metodunu çağırır.

Bu, uygulamadaki polimorfizmdir; aynı kod, tamamen farklı davranışlara sahip nesneleri işler. Yeni bir eylem türü eklemek, yineleme mantığında herhangi bir değişiklik gerektirmez; sadece diziye başka bir öğe eklersiniz.

challenge icon

Görev

Kolay

Ortak bir arayüzü paylaşan ancak farklı davranışlar sergileyen bir nesne koleksiyonunu işlemeyi, yani polimorfik yinelemeyi (polymorphic iteration) gösteren bir Task yönetim sistemi kuralım.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • task.h: Görev arayüzünüzü burada tanımlayın. Parametre almayan ve hiçbir şey döndürmeyen TaskFunc adında bir fonksiyon göstericisi (function pointer) türü oluşturun. Ardından, bir name (bir const char*) ve TaskFunc türünde bir run fonksiyon göstericisi içeren bir Task yapısı (struct) tanımlayın.
  • task.c: Farklı iş türlerini temsil eden üç farklı görev fonksiyonu uygulayın:
    • backup_taskBacking up data... yazdırır.
    • cleanup_taskCleaning up files... yazdırır.
    • report_taskGenerating report... yazdırır.
  • main.c: Her şeyi burada birleştirin. Her biri farklı bir görev fonksiyonuna bağlanmış bir Task yapısı dizisi oluşturun. Ardından dizi boyunca ilerleyin ve her görevi polimorfik olarak çalıştırın — her görev için adını yazdırın ve ardından run fonksiyonunu çağırın.

Programınız tek bir girdi alacaktır: çalıştırılacak görev sayısı (1, 2 veya 3).

Tam olarak şu sırayla üç görev içeren bir dizi oluşturun: Backup adında bir yedekleme görevi, Cleanup adında bir temizleme görevi ve Report adında bir rapor görevi. Girdi olarak alınan sayıya göre, dizinin başından itibaren sadece o kadar görev üzerinde ilerleyin.

Yinelemenizdeki her görev için, görevin adını, ardından bir iki nokta üst üste ve boşluk yazdırın, sonra run fonksiyonunu çağırın.

Girdi 2 olduğunda örnek çıktı:

Backup: Backing up data...
Cleanup: Cleaning up files...

Girdi 3 olduğunda örnek çıktı:

Backup: Backing up data...
Cleanup: Cleaning up files...
Report: Generating report...

Buradaki temel nokta, döngünüzün her bir görevin ne yaptığını bilmesine gerek olmamasıdır — sadece adı yazdırır ve her öğe üzerinde run() fonksiyonunu çağırır. Aynı yineleme kodu tüm görev türlerini tek tip olarak işler. Başlık dosyanızda include guard kullanmayı unutmayın.

Kopya kağıdı

Ortak bir arayüzü paylaşan nesne koleksiyonları üzerinde döngü kurabilir ve her bir öğenin özel uygulamasından bağımsız olarak her öğe üzerinde aynı fonksiyon işaretçisini çağırabilirsiniz. Bu polimorfizmdir (çok biçimlilik) — farklı nesnelere tek tip davranmak.

Bir fonksiyon işaretçisi türü ve bunu kullanan bir struct tanımlayın:

typedef void (*ExecuteFunc)(void);

typedef struct {
    const char* name;
    ExecuteFunc execute;
} Action;

İmzaya uygun somut fonksiyonlar oluşturun:

void jump(void) { printf("Jumping!\n"); }
void run(void)  { printf("Running!\n"); }
void rest(void) { printf("Resting...\n"); }

Her biri farklı bir fonksiyona bağlanmış bir struct dizisi oluşturun, ardından bunlar üzerinde döngü kurun:

Action actions[3] = {
    { "Jump", jump },
    { "Run", run },
    { "Rest", rest }
};

for (int i = 0; i < 3; i++) {
    printf("%s: ", actions[i].name);
    actions[i].execute();
}

Döngünün her bir eylemin dahili olarak ne yaptığını bilmesine gerek yoktur — sadece her öğe üzerindeki fonksiyon işaretçisini çağırır. Yeni türler eklemek, döngü mantığında herhangi bir değişiklik gerektirmez.

Kendin dene

#include <stdio.h>
#include "task.h"

// task.c dosyasında uygulanan görev fonksiyonlarını bildirin
void backup_task(void);
void cleanup_task(void);
void report_task(void);

int main() {
    int count;
    scanf("%d", &count);
    
    // TODO: Bu sırayla 3 Task struct'ından oluşan bir dizi oluşturun:
    // 1. backup_task kullanan "Backup" adlı yedekleme görevi
    // 2. cleanup_task kullanan "Cleanup" adlı temizleme görevi
    // 3. report_task kullanan "Report" adlı raporlama görevi
    
    // TODO: Dizideki 'count' kadar görev boyunca döngü kurun
    // Her görev için şunu yazdırın: "<name>: " ardından run fonksiyonunu çağırın
    
    return 0;
}
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Object Oriented Programming bölümündeki tüm dersler