Menu
Coddy logo textTech

Adaptör Kalıbı

Coddy'nin C++ Journey'sinin Nesne Yönelimli Programlama bölümünün bir parçası — ders 97 / 104.

Adapter deseni, uyumsuz arayüzlerin birlikte çalışmasına olanak tanır. Bir sınıfın arayüzünü istemcilerin beklediği bir arayüz haline dönüştürerek iki sınıf arasında bir köprü görevi görür. Bu, özellikle sisteminizin arayüzüyle eşleşmeyen eski kodları veya üçüncü taraf kütüphaneleri entegre ederken kullanışlıdır.

İhtiyacınız olanı yapan mevcut bir sınıfınız olduğunu, ancak arayüzünün kodunuzun beklediğiyle eşleşmediğini hayal edin. Her iki tarafı da değiştirmek yerine, mevcut sınıfı sarmalayan ve çağrıları çeviren bir adaptör oluşturursunuz:

#include <iostream>
#include <string>

// Uyumsuz arayüze sahip mevcut sınıf
class LegacyPrinter {
public:
    void printMessage(const std::string& msg) {
        std::cout << "Legacy: " << msg << "\n";
    }
};

// İstemcinin beklediği hedef arayüz
class Printer {
public:
    virtual void print(const std::string& text) = 0;
    virtual ~Printer() = default;
};

// Adaptör LegacyPrinter'ı sarmalar ve Printer arayüzünü uygular
class PrinterAdapter : public Printer {
    LegacyPrinter& legacy;
public:
    PrinterAdapter(LegacyPrinter& lp) : legacy(lp) {}
    
    void print(const std::string& text) override {
        legacy.printMessage(text);  // Çağrıyı dönüştür
    }
};

int main() {
    LegacyPrinter oldPrinter;
    PrinterAdapter adapter(oldPrinter);
    
    // İstemci beklenen arayüzü kullanır
    adapter.print("Hello World");
}

Adaptör, LegacyPrinter'a bir referans tutar ve Printer arayüzünü uygular. print() çağrıldığında, işlemi printMessage()'a devreder. İstemci kodu, alttaki eski uygulamayı bilmeden Printer arayüzü ile çalışır.

Mevcut bir sınıfı kullanmak istediğinizde ancak arayüzü ihtiyacınız olanla eşleşmediğinde veya uyumsuz arayüzlere sahip sınıflarla işbirliği yapan yeniden kullanılabilir bir sınıf oluşturmanız gerektiğinde Adapter kullanın.

challenge icon

Görev

Kolay

Farklı ses kütüphanesi arayüzlerini birbirine bağlayan bir Medya Oynatıcı Adaptörü sistemi kuralım. Eski bir ses kütüphanesini modern bir medya oynatıcı uygulamasına entegre ettiğinizi hayal edin; eski kütüphane mükemmel çalışıyor ancak arayüzü oynatıcınızın beklediğiyle eşleşmiyor. İşte bu, Adaptör (Adapter) deseninin parladığı yerdir.

Kodunuzu dört dosya halinde düzenleyeceksiniz:

  • MediaPlayer.h: Modern medya oynatıcınızın beklediği hedef arayüzü tanımlayın.

    Şu saf sanal (pure virtual) metotlara sahip soyut bir MediaPlayer sınıfı oluşturun:

    • play(const std::string& filename) — belirtilen dosyayı oynatır
    • stop() — oynatmayı durdurur
    • setVolume(int level) — ses seviyesini ayarlar (0-100)

    Sanal bir yıkıcı (virtual destructor) ekleyin.

  • LegacyAudioLib.h: Uyumsuz bir arayüze sahip mevcut "eski" ses kütüphanesini oluşturun.

    LegacyAudioLib sınıfınız, oynatıcınızın beklediğinden farklı çalışan eski bir kütüphaneyi temsil eder. Şunlara sahip olmalıdır:

    • loadAudio(const std::string& path)Legacy: Loading [path] yazdırır
    • startPlayback()Legacy: Playback started yazdırır
    • stopPlayback()Legacy: Playback stopped yazdırır
    • adjustVolume(double percentage) — ondalık bir değer (0.0 ile 1.0 arası) alır ve Legacy: Volume set to [percentage] yazdırır (ondalık değeri gösterin)
  • AudioAdapter.h: İki arayüzü birbirine bağlayan adaptörü oluşturun.

    AudioAdapter sınıfınız MediaPlayer sınıfından miras almalı ve bir LegacyAudioLib nesnesine referans tutmalıdır. Adaptör, modern arayüzden gelen çağrıları eski arayüze çevirir:

    • play(), eski kütüphane üzerinde hem loadAudio() hem de startPlayback() metotlarını çağırmalıdır
    • stop(), stopPlayback() metoduna delege edilmelidir
    • setVolume() bir tam sayı (0-100) alır ancak eski kütüphane ondalık bir değer (0.0-1.0) bekler, bu yüzden bunu dönüştürmeniz gerekecektir
  • main.cpp: Adaptörün çalışmasını gösterin.

    İki girdi okuyun:

    1. Oynatılacak bir dosya adı (string)
    2. Bir ses seviyesi (tam sayı, 0-100)

    Bir LegacyAudioLib örneği ve onu sarmalayan bir AudioAdapter oluşturun. Ardından adaptörü MediaPlayer arayüzü üzerinden (bir MediaPlayer işaretçisi olarak saklayarak) şunları yapmak için kullanın:

    1. Ses seviyesini girdi olarak alınan seviyeye ayarlayın
    2. Girdi olarak alınan dosya adını oynatın
    3. Oynatmayı durdurun

Örneğin, song.mp3 ve 75 girdileriyle:

Legacy: Volume set to 0.75
Legacy: Loading song.mp3
Legacy: Playback started
Legacy: Playback stopped

podcast.wav ve 50 girdileriyle:

Legacy: Volume set to 0.5
Legacy: Loading podcast.wav
Legacy: Playback started
Legacy: Playback stopped

İstemci kodunun tamamen MediaPlayer arayüzüyle nasıl çalıştığına dikkat edin; arka planda asıl işi eski bir kütüphanenin yaptığından haberi bile yoktur. Adaptör, tam sayı ses seviyesini ondalık sayıya dönüştürmek de dahil olmak üzere tüm çeviri işlemini gerçekleştirir. Adaptör deseninin özü budur: uyumsuz arayüzlerin her iki tarafı da değiştirmeden birlikte çalışmasını sağlamak.

Kopya kağıdı

Adapter deseni, iki sınıf arasında bir köprü görevi görerek uyumsuz arayüzlerin birlikte çalışmasını sağlar. Bir sınıfın arayüzünü, istemcilerin beklediği bir arayüze dönüştürür.

Mevcut bir sınıfı kullanmak istediğinizde ancak arayüzü ihtiyacınız olanla eşleşmediğinde veya eski kodları ya da üçüncü taraf kütüphaneleri entegre ederken Adapter desenini kullanın.

Yapı

Desen üç ana bileşenden oluşur:

  • Hedef Arayüz (Target Interface): İstemcinin beklediği arayüz
  • Adapte Edilen (Adaptee): Uyumsuz arayüze sahip mevcut sınıf
  • Adaptör (Adapter): Adapte edileni sarmalar ve hedef arayüzü uygulayarak aralarındaki çağrıları çevirir

Örnek

#include <iostream>
#include <string>

// Uyumsuz arayüze sahip mevcut sınıf (Adaptee)
class LegacyPrinter {
public:
    void printMessage(const std::string& msg) {
        std::cout << "Legacy: " << msg << "\n";
    }
};

// İstemcinin beklediği hedef arayüz
class Printer {
public:
    virtual void print(const std::string& text) = 0;
    virtual ~Printer() = default;
};

// Adapter, LegacyPrinter'ı sarmalar ve Printer arayüzünü uygular
class PrinterAdapter : public Printer {
    LegacyPrinter& legacy;
public:
    PrinterAdapter(LegacyPrinter& lp) : legacy(lp) {}
    
    void print(const std::string& text) override {
        legacy.printMessage(text);  // Çağrıyı çevir
    }
};

int main() {
    LegacyPrinter oldPrinter;
    PrinterAdapter adapter(oldPrinter);
    
    // İstemci beklenen arayüzü kullanır
    adapter.print("Hello World");
}

Adaptör, adapte edilenin bir referansını tutar ve hedef arayüzü uygular. Adaptör üzerindeki metotlar çağrıldığında, bu çağrıları gerektiği gibi çevirerek adapte edilenin metotlarına devreder. İstemci kodu, eski uygulamadan haberdar olmadan hedef arayüz ile çalışır.

Kendin dene

#include <iostream>
#include <string>
#include "MediaPlayer.h"
#include "LegacyAudioLib.h"
#include "AudioAdapter.h"

using namespace std;

int main() {
    // Girdileri oku
    string filename;
    int volumeLevel;
    cin >> filename;
    cin >> volumeLevel;
    
    // TODO: Bir LegacyAudioLib örneği oluşturun
    
    // TODO: Eski kütüphaneyi sarmalayan bir AudioAdapter oluşturun
    
    // TODO: Adaptörü bir MediaPlayer işaretçisi (pointer) olarak saklayın
    
    // TODO: MediaPlayer arayüzünü şunları yapmak için kullanın:
    // 1. Ses seviyesini giriş seviyesine ayarlayın
    // 2. Giriş dosya adını oynatın
    // 3. Oynatmayı durdurun
    
    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.

Nesne Yönelimli Programlama bölümündeki tüm dersler