Menu
Coddy logo textTech

C++'ta Arayüz Tasarımı

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

C++'da bir arayüz (interface), tüm üye fonksiyonların saf sanal (pure virtual) olduğu soyut bir sınıftır. Bazı uygulamalar içerebilen soyut sınıfların aksine, arayüzler yalnızca hangi işlemlerin var olması gerektiğini tanımlar; herhangi bir davranış olmaksızın bir sözleşme belirtirler.

class Drawable {
public:
    virtual void draw() = 0;
    virtual void resize(double factor) = 0;
    virtual ~Drawable() = default;
};

Bu Drawable arayüzü, uygulayan herhangi bir sınıfın draw() ve resize() metotlarına sahip olacağını garanti eder, ancak bunların nasıl çalıştığı hakkında hiçbir şey söylemez. Bu ayrım güçlüdür; kod, somut türler hakkında hiçbir şey bilmeden arayüze bağımlı olabilir.

class Circle : public Drawable {
    double radius;
public:
    Circle(double r) : radius(r) {}
    void draw() override { std::cout << "Drawing circle" << std::endl; }
    void resize(double factor) override { radius *= factor; }
};

class Button : public Drawable {
    std::string label;
public:
    Button(std::string l) : label(l) {}
    void draw() override { std::cout << "Drawing button: " << label << std::endl; }
    void resize(double factor) override { /* butonu yeniden boyutlandır */ }
};

Bir sınıf birden fazla arayüz uygulayabilir; bu da nesnelerin farklı rolleri üstlenebildiği esnek tasarımlara olanak tanır:

class Clickable {
public:
    virtual void onClick() = 0;
    virtual ~Clickable() = default;
};

class IconButton : public Drawable, public Clickable {
public:
    void draw() override { std::cout << "Drawing icon" << std::endl; }
    void resize(double factor) override { }
    void onClick() override { std::cout << "Clicked!" << std::endl; }
};

Arayüzler gevşek bağlılığı (loose coupling) teşvik eder; kodunuz somut uygulamalar yerine soyutlamalara dayanır, bu da genişletmeyi ve test etmeyi kolaylaştırır.

challenge icon

Görev

Kolay

Arayüzlerin farklı cihaz türleri için sözleşmeleri nasıl tanımladığını gösteren bir cihaz yönetim sistemi kuralım. Cihazların uygulayabileceği iki ayrı arayüz oluşturacak, ardından bu sözleşmelerden birini veya her ikisini yerine getiren somut cihaz sınıfları inşa edeceksiniz.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • Interfaces.h: Farklı cihaz yeteneklerini temsil eden iki saf soyut sınıf (arayüz) tanımlayın:

    Powerable — açılıp kapatılabilen herhangi bir cihaz:

    • Saf sanal bir powerOn() metodu
    • Saf sanal bir powerOff() metodu
    • std::string döndüren saf sanal bir getPowerStatus() metodu
    • Sanal bir yıkıcı (destructor)

    Connectable — bir ağa bağlanabilen herhangi bir cihaz:

    • Saf sanal bir connect(const std::string& network) metodu
    • Saf sanal bir disconnect() metodu
    • std::string döndüren saf sanal bir getConnectionInfo() metodu
    • Sanal bir yıkıcı (destructor)
  • Devices.h: Bu arayüzleri kullanan somut cihaz sınıflarını uygulayın:

    Lamp — yalnızca Powerable arayüzünü uygular:

    • Özel (private) bir bool isOn üyesi (false olarak başlar)
    • Özel (private) bir std::string name üyesi
    • Lambanın adını alan bir kurucu (constructor)
    • powerOn(), isOn değerini true yapar ve şunu yazdırır: <name>: Light turned on
    • powerOff(), isOn değerini false yapar ve şunu yazdırır: <name>: Light turned off
    • getPowerStatus(), duruma göre "ON" veya "OFF" döndürür

    SmartTV — hem Powerable hem de Connectable arayüzlerini uygular:

    • Özel (private) üyeler: bool isOn (false olarak başlar), std::string brand, std::string currentNetwork (boş olarak başlar)
    • TV'nin markasını alan bir kurucu (constructor)
    • powerOn(), isOn değerini true yapar ve şunu yazdırır: <brand> TV: Powered on
    • powerOff(), isOn değerini false yapar, ağı temizler ve şunu yazdırır: <brand> TV: Powered off
    • getPowerStatus(), "ON" veya "OFF" döndürür
    • connect(), ağ adını saklar ve şunu yazdırır: <brand> TV: Connected to <network>
    • disconnect(), ağı temizler ve şunu yazdırır: <brand> TV: Disconnected
    • getConnectionInfo(), bağlıysa "Connected to <network>", boşsa "Not connected" döndürür
  • main.cpp: Üç girdi okuyun (her biri ayrı bir satırda):
    1. Lamba adı
    2. TV markası
    3. Ağ adı

    Bir Lamp ve bir SmartTV oluşturun. Aynı arayüzün farklı cihazlarla nasıl kullanılabileceğini gösterin:

    İlk olarak, her iki cihazla da Powerable arayüzü üzerinden çalışın. Her ikisinin işaretçilerini (pointer) bir Powerable* dizisinde saklayın, ardından döngü ile her biri için powerOn() metodunu çağırın ve durumlarını şu şekilde yazdırın: Status: <powerStatus>

    Boş bir satır yazdırın, ardından SmartTV ile Connectable arayüzü üzerinden çalışın. SmartTV'niz için bir Connectable* işaretçisi oluşturun, ağ adıyla connect() metodunu çağırın ve şunu yazdırın: Connection: <connectionInfo>

    Başka bir boş satır yazdırın, ardından Powerable dizisi üzerinden her iki cihazı da kapatın ve son durumlarını gösterin.

Örneğin, Desk Lamp, Samsung ve HomeWiFi girdileriyle:

Desk Lamp: Light turned on
Status: ON
Samsung TV: Powered on
Status: ON

Samsung TV: Connected to HomeWiFi
Connection: Connected to HomeWiFi

Desk Lamp: Light turned off
Status: OFF
Samsung TV: Powered off
Status: OFF

SmartTV'nin, hangi arayüz işaretçisini kullandığınıza bağlı olarak hem Powerable hem de Connectable olarak nasıl ele alınabildiğine dikkat edin. Bu esneklik, birden fazla arayüz uygulamanın gücüdür; kodunuz, somut türü bilmesine gerek kalmadan, ihtiyaç duyduğu sözleşmeyi yerine getiren herhangi bir cihazla çalışabilir.

Kopya kağıdı

C++'ta bir arayüz (interface), tüm üye fonksiyonların saf sanal (pure virtual) olduğu soyut bir sınıftır. Arayüzler, herhangi bir uygulama (implementation) içermeden yalnızca hangi işlemlerin var olması gerektiğini tanımlar:

class Drawable {
public:
    virtual void draw() = 0;
    virtual void resize(double factor) = 0;
    virtual ~Drawable() = default;
};

Sınıflar, tüm saf sanal fonksiyonları geçersiz kılarak (overriding) arayüzleri uygularlar:

class Circle : public Drawable {
    double radius;
public:
    Circle(double r) : radius(r) {}
    void draw() override { std::cout << "Drawing circle" << std::endl; }
    void resize(double factor) override { radius *= factor; }
};

Bir sınıf, çoklu kalıtım (multiple inheritance) kullanarak birden fazla arayüzü uygulayabilir:

class Clickable {
public:
    virtual void onClick() = 0;
    virtual ~Clickable() = default;
};

class IconButton : public Drawable, public Clickable {
public:
    void draw() override { std::cout << "Drawing icon" << std::endl; }
    void resize(double factor) override { }
    void onClick() override { std::cout << "Clicked!" << std::endl; }
};

Arayüzler, kodun somut uygulamalar yerine soyutlamalara dayanmasını sağlayarak gevşek bağlılığı (loose coupling) teşvik eder.

Kendin dene

#include <iostream>
#include <string>
#include "Devices.h"

using namespace std;

int main() {
    // Girdileri oku
    string lampName;
    string tvBrand;
    string networkName;
    
    getline(cin, lampName);
    getline(cin, tvBrand);
    getline(cin, networkName);
    
    // TODO: Bir Lamp ve bir SmartTV nesnesi oluşturun
    
    // TODO: Her iki cihazı da içeren bir Powerable* işaretçi dizisi oluşturun
    
    // TODO: Dizi boyunca döngü kurun ve her cihazda powerOn() fonksiyonunu çağırın
    // Her powerOn() işleminden sonra şunu yazdırın: Status: <powerStatus>
    
    // TODO: Boş bir satır yazdırın
    
    // TODO: SmartTV için bir Connectable* işaretçisi oluşturun
    // Ağ adıyla connect() fonksiyonunu çağırın
    // Yazdırın: Connection: <connectionInfo>
    
    // TODO: Boş bir satır yazdırın
    
    // TODO: Powerable dizisi boyunca döngü kurun ve her cihazda powerOff() fonksiyonunu çağırın
    // Her powerOff() işleminden sonra şunu yazdırın: Status: <powerStatus>
    
    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