Menu
Coddy logo textTech

Dekoratör Kalıbı

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

Decorator deseni, nesneleri dekoratör adı verilen özel nesnelerle sarmalayarak onlara dinamik olarak yeni davranışlar eklemenize olanak tanır. Derleme zamanında davranış ekleyen kalıtımın aksine, dekoratörler orijinal sınıfı değiştirmeden çalışma zamanında işlevselliği genişletmenize izin verir.

Desen, hem orijinal nesnenin hem de dekoratörlerin aynı arayüzü (interface) uygulamasıyla çalışır. Her dekoratör bir bileşene (component) referans tutar ve sarmalanmış nesneye devretmeden önce veya sonra kendi davranışını ekler:

#include <iostream>
#include <memory>

// Bileşen arayüzü
class Coffee {
public:
    virtual std::string getDescription() const = 0;
    virtual double getCost() const = 0;
    virtual ~Coffee() = default;
};

// Somut bileşen
class SimpleCoffee : public Coffee {
public:
    std::string getDescription() const override { return "Coffee"; }
    double getCost() const override { return 2.0; }
};

// Temel dekoratör
class CoffeeDecorator : public Coffee {
protected:
    std::unique_ptr<Coffee> coffee;
public:
    CoffeeDecorator(std::unique_ptr<Coffee> c) : coffee(std::move(c)) {}
};

// Somut dekoratörler
class MilkDecorator : public CoffeeDecorator {
public:
    MilkDecorator(std::unique_ptr<Coffee> c) : CoffeeDecorator(std::move(c)) {}
    std::string getDescription() const override {
        return coffee->getDescription() + ", Milk";
    }
    double getCost() const override { return coffee->getCost() + 0.5; }
};

class SugarDecorator : public CoffeeDecorator {
public:
    SugarDecorator(std::unique_ptr<Coffee> c) : CoffeeDecorator(std::move(c)) {}
    std::string getDescription() const override {
        return coffee->getDescription() + ", Sugar";
    }
    double getCost() const override { return coffee->getCost() + 0.2; }
};

Dekoratörler üst üste eklenebilir - her biri bir öncekini sarmalayarak işlevselliği katman katman oluşturur:

int main() {
    std::unique_ptr<Coffee> order = std::make_unique<SimpleCoffee>();
    order = std::make_unique<MilkDecorator>(std::move(order));
    order = std::make_unique<SugarDecorator>(std::move(order));
    
    std::cout << order->getDescription() << ": $" << order->getCost();
    // Çıktı: Coffee, Milk, Sugar: $2.7
}

Diğer nesneleri etkilemeden nesnelere dinamik olarak sorumluluklar eklemeniz gerektiğinde veya alt sınıflar aracılığıyla işlevselliği genişletmek sınıf sayısında aşırı artışa neden olacağı durumlarda Decorator kullanın.

challenge icon

Görev

Kolay

Decorator desenini kullanarak bir Pizza Sipariş Sistemi oluşturalım. Müşterilerin temel bir pizza ile başlayıp çeşitli malzemeler ekleyebileceği esnek bir sistem oluşturacaksınız; her malzeme bir önceki siparişi sarmalar ve kendi maliyetini ve açıklamasını ekler. Bu, dekoratörler için mükemmel bir kullanım durumudur: malzemeler, alt sınıfların aşırı çoğalmasına neden olmadan herhangi bir kombinasyonda üst üste eklenebilir.

Kodunuzu üç dosya halinde düzenleyeceksiniz:

  • Pizza.h: Bileşen arayüzünü ve temel pizza sınıfını tanımlayın.

    Saf sanal metotlar olan getDescription() (bir string döndürür) ve getCost() (bir double döndürür) ile birlikte sanal bir yıkıcıya (destructor) sahip soyut bir Pizza sınıfı oluşturun.

    Ardından, temel pizzayı temsil eden somut bir PlainPizza sınıfı oluşturun. Açıklama olarak "Pizza" döndürmeli ve 8.0 temel maliyetine sahip olmalıdır.

  • ToppingDecorator.h: Dekoratör temel sınıfını ve somut malzeme dekoratörlerini oluşturun.

    Pizza sınıfından miras alan ve sarmalanmış bileşene bir std::unique_ptr<Pizza> tutan bir ToppingDecorator sınıfı oluşturun. Bu, tüm malzeme dekoratörleri için temel görevi görür.

    Üç somut malzeme dekoratörü uygulayın:

    • CheeseTopping — açıklamaya ", Cheese" ve maliyete 1.5 ekler
    • PepperoniTopping — açıklamaya ", Pepperoni" ve maliyete 2.0 ekler
    • MushroomTopping — açıklamaya ", Mushrooms" ve maliyete 1.0 ekler

    Her dekoratör, sarmalanmış pizzaya delege etmeli ve ardından kendi katkısını eklemelidir.

  • main.cpp: Kullanıcı girişine göre özelleştirilmiş bir pizza siparişi oluşturun.

    Üç giriş okuyun (her biri yes veya no olacaktır):

    1. Peynir eklensin mi?
    2. Sucuk (pepperoni) eklensin mi?
    3. Mantar eklensin mi?

    Bir PlainPizza ile başlayın, ardından girişlere göre (sırasıyla: peynir, sucuk, mantar) uygun malzeme dekoratörleriyle sarmalayın. Son olarak, sipariş detaylarını şu formatta yazdırın:

    Order: [description]
    Total: $[cost]

Örneğin, yes, yes, no girişleri ile:

Order: Pizza, Cheese, Pepperoni
Total: $11.5

yes, no, yes girişleri ile:

Order: Pizza, Cheese, Mushrooms
Total: $10.5

no, no, no girişleri ile:

Order: Pizza
Total: $8

yes, yes, yes girişleri ile:

Order: Pizza, Cheese, Pepperoni, Mushrooms
Total: $12.5

Her dekoratörün bir öncekini nasıl sarmaladığına, hem açıklamayı hem de maliyeti katman katman nasıl oluşturduğuna dikkat edin. Mevcut pizza veya malzeme kodunu değiştirmeden yeni dekoratör sınıfları oluşturarak kolayca yeni malzemeler ekleyebilirsiniz; Decorator deseninin güzelliği de budur.

Kendin dene

#include <iostream>
#include <string>
#include <memory>
#include "Pizza.h"
#include "ToppingDecorator.h"

using namespace std;

int main() {
    // Üç girdi oku (yes veya no)
    string addCheese, addPepperoni, addMushrooms;
    cin >> addCheese;
    cin >> addPepperoni;
    cin >> addMushrooms;
    
    // YAPILACAK: std::unique_ptr kullanarak bir PlainPizza ile başla
    
    // YAPILACAK: addCheese "yes" ise, pizzayı CheeseTopping ile sarmala
    
    // YAPILACAK: addPepperoni "yes" ise, pizzayı PepperoniTopping ile sarmala
    
    // YAPILACAK: addMushrooms "yes" ise, pizzayı MushroomTopping ile sarmala
    
    // YAPILACAK: Siparişi şu formatta yazdır:
    // Order: [description]
    // Total: $[cost]
    
    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