Pola Decorator
Termasuk bagian Pemrograman Berorientasi Object dalam perjalanan C++ di Coddy. Pelajaran 98 dari 104.
Pola Decorator memungkinkan kamu menambahkan perilaku baru ke object secara dinamis dengan membungkusnya dalam object khusus yang disebut decorator. Tidak seperti pewarisan, yang menambahkan perilaku saat kompilasi, decorator memungkinkan kamu memperluas fungsionalitas saat runtime tanpa mengubah class asli.
Pola ini bekerja dengan membuat object asli dan decorator mengimplementasikan interface yang sama. Setiap decorator menyimpan referensi ke sebuah komponen dan menambahkan perilakunya sendiri sebelum atau sesudah meneruskan panggilan ke object yang dibungkus:
#include <iostream>
#include <memory>
// Antarmuka komponen
class Coffee {
public:
virtual std::string getDescription() const = 0;
virtual double getCost() const = 0;
virtual ~Coffee() = default;
};
// Komponen konkret
class SimpleCoffee : public Coffee {
public:
std::string getDescription() const override { return "Coffee"; }
double getCost() const override { return 2.0; }
};
// Dekorator dasar
class CoffeeDecorator : public Coffee {
protected:
std::unique_ptr<Coffee> coffee;
public:
CoffeeDecorator(std::unique_ptr<Coffee> c) : coffee(std::move(c)) {}
};
// Dekorator konkret
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 dapat ditumpuk, setiap decorator membungkus decorator sebelumnya, sehingga fungsionalitas bertambah lapis demi lapis:
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();
// output: Coffee, Milk, Sugar: $2.7
}Gunakan Decorator saat kamu perlu menambahkan tanggung jawab ke object secara dinamis tanpa memengaruhi object lain, atau saat memperluas fungsionalitas melalui subclassing akan menyebabkan jumlah class meningkat drastis.
Tantangan
MudahMari kita bangun Sistem Pemesanan Pizza menggunakan pola Decorator. Kamu akan membuat sistem fleksibel yang memungkinkan pelanggan memulai dengan pizza dasar dan menambahkan berbagai topping: setiap topping membungkus pesanan sebelumnya dan menambahkan biaya serta deskripsinya sendiri. Ini adalah kasus penggunaan yang sempurna untuk decorator: topping dapat ditumpuk dalam kombinasi apa pun tanpa membuat jumlah subclass membengkak.
Kamu akan mengatur kodemu dalam tiga file:
Pizza.h: Definisikan interface komponen dan class pizza dasar.Buat class abstrak
Pizzadengan method virtual murnigetDescription()(mengembalikan string) dangetCost()(mengembalikan double), serta destructor virtual.Lalu buat class konkret
PlainPizzayang merepresentasikan pizza dasar. Class ini harus mengembalikan"Pizza"sebagai deskripsinya dan memiliki biaya dasar8.0.ToppingDecorator.h: Buat class dasar decorator dan decorator topping konkret.Buat class
ToppingDecoratoryang mewarisiPizzadan menyimpanstd::unique_ptr<Pizza>ke komponen yang dibungkus. Class ini menjadi dasar untuk semua decorator topping.Implementasikan tiga decorator topping konkret:
CheeseTopping: menambahkan", Cheese"ke deskripsi dan1.5ke biayaPepperoniTopping: menambahkan", Pepperoni"ke deskripsi dan2.0ke biayaMushroomTopping: menambahkan", Mushrooms"ke deskripsi dan1.0ke biaya
Setiap decorator harus meneruskan pemanggilan ke pizza yang dibungkus, lalu menambahkan kontribusinya sendiri.
main.cpp: Buat pesanan pizza yang disesuaikan berdasarkan input pengguna.Baca tiga input (masing-masing berupa
yesatauno):- Tambahkan keju?
- Tambahkan pepperoni?
- Tambahkan jamur?
Mulai dengan
PlainPizza, lalu bungkus dengan decorator topping yang sesuai berdasarkan input (dengan urutan: keju, pepperoni, jamur). Terakhir, cetak detail pesanan dengan format berikut:Order: [description] Total: $[cost]
Contohnya, dengan input yes, yes, no:
Order: Pizza, Cheese, Pepperoni
Total: $11.5Dengan input yes, no, yes:
Order: Pizza, Cheese, Mushrooms
Total: $10.5Dengan input no, no, no:
Order: Pizza
Total: $8Dengan input yes, yes, yes:
Order: Pizza, Cheese, Pepperoni, Mushrooms
Total: $12.5Perhatikan bagaimana setiap decorator membungkus decorator sebelumnya, menyusun deskripsi dan biaya lapis demi lapis. Kamu dapat dengan mudah menambahkan topping baru dengan membuat class decorator baru tanpa mengubah kode pizza atau topping yang sudah ada. Itulah keunggulan pola Decorator.
Coba sendiri
#include <iostream>
#include <string>
#include <memory>
#include "Pizza.h"
#include "ToppingDecorator.h"
using namespace std;
int main() {
// Baca tiga input (yes atau no)
string addCheese, addPepperoni, addMushrooms;
cin >> addCheese;
cin >> addPepperoni;
cin >> addMushrooms;
// TODO: Mulai dengan PlainPizza menggunakan std::unique_ptr
// TODO: Jika addCheese bernilai "yes", bungkus pizza dengan CheeseTopping
// TODO: Jika addPepperoni bernilai "yes", bungkus pizza dengan PepperoniTopping
// TODO: Jika addMushrooms bernilai "yes", bungkus pizza dengan MushroomTopping
// TODO: Tampilkan pesanan dalam format:
// Pesanan: [description]
// Total: $[cost]
return 0;
}
Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Object
1Dasar-Dasar OOP
File EksternalBuild dan Kompilasi C++File Header dan File SourceNamespace dan CakupanPengantar OOP dalam C++class vs objectPointer 'this'method (Fungsi Anggota)Atribut (Anggota Data)Dasar Ctor dan DtorRingkasan: Kalkulator Sederhana4Properti class
Anggota instance vs staticGetter dan setterFungsi anggota constKata kunci mutableMethod dan variabel staticFungsi dan class friendRangkuman, Pengelola Rekening Bank7Pewarisan
Pewarisan DasarTingkat Akses pada PewarisanUrutan Pemanggilan Ctor & DtorPenimpaan methodFungsi Virtual & VTablePewarisan BergandaPewarisan VirtualRangkuman: Hierarki Karyawan2Manajemen Memori
Memori Stack vs HeapPointer dan ReferenceMemori Dinamis (new/delete)Smart Pointer di C++RAII di C++Rangkuman: Pengelola Array Dinamis5Enkapsulasi
Penentu Akses dalam C++Penentu Akses secara MendalamPenyembunyian Informasistruct vs classNested dan Inner classRangkuman, Sistem Catatan Siswa8Polimorfisme
Polimorfisme saat Compile vs RuntimeOverloading FungsiMeninjau Kembali Fungsi VirtualFungsi Virtual MurniKelas AbstrakDesain Interface di C++Casting Dinamis & RTTIRangkuman: Kalkulator Bentuk11Konsep OOP Lanjutan
Komposisi vs PewarisanMixin melalui CRTPIdiom PimplPenghapusan TipeKelas Enum dan Pengetikan KuatPenanganan Pengecualian dalam OOPHierarki Pengecualian Kustom14Pola Desain Bagian 2
Pola CommandPola AdapterPola DecoratorPola Template MethodPola StatePola CompositeRAII sebagai Pola3Konstruktor & Destruktor
Konstruktor DefaultKonstruktor BerparameterKonstruktor SalinKonstruktor PindahDaftar Inisialisasi KonstruktorKonstruktor DelegasiKupas Tuntas DestruktorAturan Tiga, Lima, dan NolRangkuman: class String6Overloading Operator
Pengantar Overloading OperatorOverloading Operator AritmetikaOverloading Operator PerbandinganOperator StreamOverloading Operator PenugasanOverloading Operator [] dan ()Operator Konversi TipeRingkasan, class Matrix9Template
Template FungsiTemplate classSpesialisasi TemplateTemplate VariadikDasar SFINAE & Type TraitsRangkuman, Container GenerikLatihan sendiri: Compiler C++ online