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 (Dekoratör) 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
}Nesnelere, diğer nesneleri etkilemeden dinamik olarak sorumluluklar eklemeniz gerektiğinde veya alt sınıflara ayırma (subclassing) yoluyla işlevselliği genişletmenin sınıf sayısında aşırı artışa neden olacağı durumlarda Decorator kullanın.
Görev
KolayDecorator 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 patlaması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ı
getDescription()(bir string döndürür) vegetCost()(bir double döndürür) olan soyut birPizzasınıfı ve bir sanal yıkıcı (destructor) oluşturun.Ardından, temel pizzayı temsil eden somut bir
PlainPizzasınıfı oluşturun. Açıklama olarak"Pizza"döndürmeli ve8.0temel maliyetine sahip olmalıdır.ToppingDecorator.h: Dekoratör temel sınıfını ve somut malzeme dekoratörlerini oluşturun.Pizzasınıfından miras alan ve sarmalanmış bileşene birstd::unique_ptr<Pizza>tutan birToppingDecoratorsı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 maliyete1.5eklerPepperoniTopping— açıklamaya", Pepperoni"ve maliyete2.0eklerMushroomTopping— açıklamaya", Mushrooms"ve maliyete1.0ekler
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
yesveyanoolacaktır):- Peynir eklensin mi?
- Sucuk (pepperoni) eklensin mi?
- Mantar eklensin mi?
Bir
PlainPizzaile başlayın, ardından girişlere göre (sırasıyla: peynir, sucuk, mantar) uygun malzeme dekoratörleriyle sarmalayın. Son olarak, sipariş ayrıntılarını şu formatta yazdırın:Order: [description] Total: $[cost]
Örneğin, yes, yes, no girişleri ile:
Order: Pizza, Cheese, Pepperoni
Total: $11.5yes, no, yes girişleri ile:
Order: Pizza, Cheese, Mushrooms
Total: $10.5no, no, no girişleri ile:
Order: Pizza
Total: $8yes, yes, yes girişleri ile:
Order: Pizza, Cheese, Pepperoni, Mushrooms
Total: $12.5Her 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 budur.
Kopya kağıdı
Decorator deseni, nesneleri dekoratör adı verilen özel nesnelerin içine sararak onlara dinamik olarak yeni davranışlar eklemenize olanak tanır. Davranışı derleme zamanında 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ü uygulamasıyla çalışır. Her dekoratör bir bileşene (component) referans tutar ve sarılmış nesneye devretmeden önce veya sonra kendi davranışını ekler.
Yapı
Bileşen arayüzü (Component interface): Hem somut bileşenler hem de dekoratörler için ortak arayüzü tanımlar.
class Coffee {
public:
virtual std::string getDescription() const = 0;
virtual double getCost() const = 0;
virtual ~Coffee() = default;
};Somut bileşen (Concrete component): Dekore edilebilen temel nesne.
class SimpleCoffee : public Coffee {
public:
std::string getDescription() const override { return "Coffee"; }
double getCost() const override { return 2.0; }
};Temel dekoratör (Base decorator): Bir bileşene referans tutar ve aynı arayüzü uygular.
class CoffeeDecorator : public Coffee {
protected:
std::unique_ptr<Coffee> coffee;
public:
CoffeeDecorator(std::unique_ptr<Coffee> c) : coffee(std::move(c)) {}
};Somut dekoratörler (Concrete decorators): Sarılmış nesneye devrederek ve kendi işlevselliklerini ekleyerek belirli davranışlar eklerler.
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; }
};Dekoratörleri Üst Üste Ekleme
Dekoratörler üst üste eklenebilir; her biri bir öncekini sararak işlevselliği katman katman oluşturur:
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.7Ne Zaman Kullanılır?
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ğında Decorator desenini kullanın.
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;
// TODO: std::unique_ptr kullanarak bir PlainPizza ile başla
// TODO: Eğer addCheese "yes" ise, pizzayı CheeseTopping ile sarmala
// TODO: Eğer addPepperoni "yes" ise, pizzayı PepperoniTopping ile sarmala
// TODO: Eğer addMushrooms "yes" ise, pizzayı MushroomTopping ile sarmala
// TODO: Siparişi şu formatta yazdır:
// Order: [description]
// Total: $[cost]
return 0;
}
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
1OOP Temelleri
Harici DosyalarC++ Build ve DerlemeBaşlık Dosyaları ve Kaynak DosyalarıAd Alanları ve KapsamC++'ta OOP'ye GirişSınıflar ve Nesneler'this' İşaretçisiMetotlar (Üye Fonksiyonlar)Öznitelikler (Veri Üyeleri)Ctor ve Dtor TemelleriÖzet - Basit Hesap Makinesi4Sınıf Özellikleri
Örnek ve Statik ÜyelerGetter ve Setter MetotlarıConst Üye FonksiyonlarMutable Anahtar KelimesiStatik Metotlar ve DeğişkenlerFriend Fonksiyonlar ve SınıflarÖzet - Banka Hesabı Yöneticisi7Kalıtım
Temel KalıtımKalıtım Erişim SeviyeleriCtor ve Dtor Çağrılma SırasıMetot Geçersiz KılmaSanal Fonksiyonlar ve VTableÇoklu KalıtımSanal KalıtımÖzet - Çalışan Hiyerarşisi10STL Genel Bakış
STL Genel Bakış ve FelsefesiSTL KonteynerleriİteratörlerSTL AlgoritmalarıFunctor'lar ve Lambda İfadeleriÖzet - Kelime Frekansı13Tasarım Kalıpları 1. Bölüm
Tasarım Kalıplarına GirişSingleton KalıbıFactory ve Abstract FactoryBuilder KalıbıObserver KalıbıStrategy Kalıbı2Bellek Yönetimi
Stack ve Heap Bellekİşaretçiler ve ReferanslarDinamik Bellek (new/delete)C++'ta Akıllı İşaretçilerC++'ta RAIIÖzet - Dinamik Dizi Yöneticisi5Kapsülleme
C++'da Erişim BelirleyicilerDerinlemesine Erişim BelirleyicilerBilgi GizlemeStruct vs Classİç İçe ve Dahili SınıflarÖzet - Öğrenci Kayıt Sistemi8Çok Biçimlilik
Derleme ve Çalışma Zamanı Çok BiçimliliğiFonksiyon Aşırı YüklemeSanal Fonksiyonlara Yeniden BakışSaf Sanal FonksiyonlarSoyut SınıflarC++'ta Arayüz TasarımıDynamic Casting ve RTTIÖzet - Şekil Hesaplayıcı11İleri Düzey OOP Kavramları
Kompozisyon ve Kalıtım KarşılaştırmasıCRTP ile Mixin YapılarıPimpl İdiyomuTür Silme (Type Erasure)Enum Sınıfları ve Güçlü TiplendirmeOOP'de İstisna YönetimiÖzel İstisna Hiyerarşileri14Tasarım Kalıpları Bölüm 2
Komut KalıbıAdaptör KalıbıDekoratör KalıbıŞablon Metot KalıbıDurum KalıbıKompozit KalıbıBir Kalıp Olarak RAII3Yapıcılar ve Yıkıcılar
Varsayılan YapıcıParametreli YapıcıKopya YapıcıTaşıma YapıcısıYapıcı İlklendirme ListeleriTemsilci YapıcılarYıkıcılara Derinlemesine BakışÜç / Beş / Sıfır KuralıÖzet - String Sınıfı6Operatör Aşırı Yükleme
Operatör Aşırı Yüklemeye GirişAritmetik Operatör Aşırı YüklemeKarşılaştırma Operatörü Aşırı YüklemeStream OperatörleriAtama Operatörü Aşırı Yükleme[] ve () Operatör Aşırı YüklemeTip Dönüşüm OperatörleriÖzet - Matris Sınıfı9Şablonlar
Fonksiyon ŞablonlarıSınıf ŞablonlarıŞablon ÖzelleştirmeVariadic ŞablonlarSFINAE ve Type Traits TemelleriÖzet - Generic Konteyner