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 kalıbı, nesneleri dekoratör adı verilen özel nesnelere sararak onlara dinamik olarak yeni davranışlar eklemenizi sağlar. Derleme zamanında davranış ekleyen kalıtımın aksine dekoratörler, özgün sınıfı değiştirmeden işlevselliği çalışma zamanında genişletmenize olanak tanır.
Bu desen, hem özgün nesnenin hem de dekoratörlerin aynı arayüzü uygulamasıyla çalışır. Her dekoratör bir bileşene başvuru tutar ve sarmalanan nesneye devretmeden önce veya devrettikten 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; }
};Decorator'lar üst üste eklenebilir; her biri bir öncekini sararak 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
}Decorator'ı, diğer nesneleri etkilemeden nesnelere dinamik olarak sorumluluklar eklemeniz gerektiğinde veya işlevselliği alt sınıflandırma yoluyla genişletmenin sınıfların aşırı artmasına yol açacağı durumlarda kullanın.
Görev
KolayDecorator desenini kullanarak bir Pizza Sipariş Sistemi oluşturalım. Müşterilerin temel bir pizzayla başlayıp çeşitli malzemeler ekleyebileceği esnek bir sistem oluşturacaksınız: her malzeme önceki siparişi sarar ve kendi maliyetini ve açıklamasını ekler. Bu, dekoratörler için mükemmel bir kullanım alanıdır: alt sınıfların patlamasına yol açmadan malzemeler herhangi bir kombinasyonla üst üste eklenebilir.
Kodunuzu üç dosyaya ayıracaksınız:
Pizza.h: Bileşen arayüzünü ve temel pizza sınıfını tanımlayın.Saf sanal
Pizza(bir string döndürür) vegetDescription()(bir double döndürür) metotlarına ve sanal bir yıkıcıya sahip soyut birgetCost()sınıfı oluşturun.Ardından temel pizzayı temsil eden somut bir
PlainPizzasınıfı oluşturun. Bu sınıf, açıklaması olarak"Pizza"döndürmeli ve temel maliyeti8.0olmalıdır.ToppingDecorator.h: Dekoratör temel sınıfını ve somut malzeme dekoratörlerini oluşturun.ToppingDecoratorsınıfından kalıtım alan ve sarılan bileşen için birPizzatutan birstd::unique_ptr<Pizza>sınıfı oluşturun. Bu sınıf, 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, sarılan pizzaya devretmeli ve ardından kendi katkısını eklemelidir.
main.cpp: Kullanıcı girdisine göre özelleştirilmiş bir pizza siparişi oluşturun.Üç girdi okuyun (her biri
yesveyanoolacaktır):- Peynir eklensin mi?
- Pepperoni eklensin mi?
- Mantar eklensin mi?
Bir
PlainPizzaile başlayın, ardından girdilere göre uygun malzeme dekoratörleriyle (sırasıyla: peynir, pepperoni, mantar) sarın. Son olarak sipariş ayrıntılarını şu biçimde yazdırın:Order: [description] Total: $[cost]
Örneğin, girdiler yes, yes, no olduğunda:
Order: Pizza, Cheese, Pepperoni
Total: $11.5Girdiler yes, no, yes olduğunda:
Order: Pizza, Cheese, Mushrooms
Total: $10.5Girdiler no, no, no olduğunda:
Order: Pizza
Total: $8Girdiler yes, yes, yes olduğunda:
Order: Pizza, Cheese, Pepperoni, Mushrooms
Total: $12.5Her dekoratörün bir öncekini sararak hem açıklamayı hem de maliyeti katman katman 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.
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 sar
// TODO: Eğer addPepperoni "yes" ise, pizzayı PepperoniTopping ile sar
// TODO: Eğer addMushrooms "yes" ise, pizzayı MushroomTopping ile sar
// 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 KonteynerKendi başına pratik yap: Online C++ derleyicisi