Pola Decorator
Termasuk bagian Pemrograman Berorientasi Objek dalam perjalanan Python di Coddy. Pelajaran 51 dari 64.
Pola Decorator menambahkan fungsionalitas baru ke object secara dinamis tanpa mengubah strukturnya. Pola ini membungkus object untuk memperluas perilakunya.
Berikut contoh kopi sederhana:
class Coffee:
def cost(self):
return 5
def description(self):
return "Simple coffee"Buat decorator yang menambahkan fitur pada kopi:
class MilkDecorator:
def __init__(self, coffee):
self.coffee = coffee
def cost(self):
return self.coffee.cost() + 2
def description(self):
return self.coffee.description() + " + Milk"
class SugarDecorator:
def __init__(self, coffee):
self.coffee = coffee
def cost(self):
return self.coffee.cost() + 1
def description(self):
return self.coffee.description() + " + Sugar"Setiap decorator membungkus object lain dan menambahkan fungsionalitasnya sendiri.
Gunakan decorator untuk membuat kopi sesuai keinginanmu:
# Mulai dengan kopi biasa
my_coffee = Coffee()
print(f"{my_coffee.description()}: ${my_coffee.cost()}")
# Tambahkan susu
my_coffee = MilkDecorator(my_coffee)
print(f"{my_coffee.description()}: ${my_coffee.cost()}")
# Tambahkan gula
my_coffee = SugarDecorator(my_coffee)
print(f"{my_coffee.description()}: ${my_coffee.cost()}")Buat contoh lain dengan pemformatan teks:
class Text:
def __init__(self, content):
self.content = content
def render(self):
return self.content
class BoldDecorator:
def __init__(self, text):
self.text = text
def render(self):
return f"<b>{self.text.render()}</b>"
class ItalicDecorator:
def __init__(self, text):
self.text = text
def render(self):
return f"<i>{self.text.render()}</i>"
# Buat teks berformat langkah demi langkah
message = Text("Hello World")
message = BoldDecorator(message)
message = ItalicDecorator(message)
print(message.render())Output:
Simple coffee: $5
Simple coffee + Milk: $7
Simple coffee + Milk + Sugar: $8
<i><b>Hello World</b></i>Poin Utama: Pola Decorator membungkus object untuk menambahkan perilaku baru tanpa mengubah object asli. Setiap decorator menyimpan referensi ke object yang dibungkus dan menambahkan fungsionalitasnya sendiri. Dengan begitu, kamu dapat menggabungkan beberapa decorator untuk mendapatkan kombinasi fitur yang fleksibel tanpa membuat banyak subclass.
Tantangan
SedangDalam tantangan ini, kamu akan menerapkan pola desain Decorator untuk membuat sistem pemesanan kopi yang fleksibel untuk kedai kopi. Pola Decorator memungkinkan perilaku ditambahkan secara dinamis ke masing-masing object, tanpa memengaruhi perilaku object lain dari class yang sama.
Kamu sedang membangun sistem untuk kedai kopi yang perlu:
- Menawarkan berbagai jenis minuman (Espresso, Dark Roast, House Blend, Decaf)
- Memungkinkan pelanggan menambahkan pelengkap (Milk, Mocha, Soy, Whipped Cream) ke minuman mereka
- Menghitung biaya minuman dengan pelengkap yang ditambahkan
- Lengkapi class abstrak
Beveragedan implementasi konkretnya dibeverage.py - Terapkan class abstrak
CondimentDecoratordan decorator konkret dicondiment_decorator.py - Terapkan skenario pengujian di
driver.pyuntuk memverifikasi implementasimu
Coba sendiri
# Impor semua class yang diperlukan
from beverage import Espresso, HouseBlend, DarkRoast, Decaf
from condiment_decorator import Milk, Mocha, Soy, Whip
# Penangan kasus uji menyeluruh
test_case = input()
if test_case == "basic_beverage_test":
# Uji fungsi dasar minuman
espresso = Espresso()
print(f"Description: {espresso.get_description()}")
print(f"Cost: ${espresso.cost():.2f}")
elif test_case == "all_beverages_test":
# Uji semua jenis minuman
beverages = [Espresso(), HouseBlend(), DarkRoast(), Decaf()]
for beverage in beverages:
print(f"{beverage.get_description()}: ${beverage.cost():.2f}")
elif test_case == "single_condiment_test":
# Uji penambahan satu pelengkap pada minuman
espresso = Espresso()
espresso_with_milk = Milk(espresso)
print(f"Description: {espresso_with_milk.get_description()}")
print(f"Cost: ${espresso_with_milk.cost():.2f}")
elif test_case == "multiple_condiments_test":
# Uji penambahan beberapa pelengkap pada minuman
house_blend = HouseBlend()
decorated_coffee = Mocha(Soy(Whip(house_blend)))
print(f"Description: {decorated_coffee.get_description()}")
print(f"Cost: ${decorated_coffee.cost():.2f}")
elif test_case == "all_condiments_test":
# Uji semua pelengkap pada satu minuman
dark_roast = DarkRoast()
fully_loaded = Milk(Mocha(Soy(Whip(dark_roast))))
print(f"Description: {fully_loaded.get_description()}")
print(f"Cost: ${fully_loaded.cost():.2f}")
elif test_case == "cost_calculation_test":
# Uji perhitungan biaya untuk berbagai kombinasi
espresso = Espresso()
# Penambahan satu pelengkap
with_milk = Milk(espresso)
with_mocha = Mocha(espresso)
print(f"Espresso: ${espresso.cost():.2f}")
print(f"Espresso + Milk: ${with_milk.cost():.2f}")
print(f"Espresso + Mocha: ${with_mocha.cost():.2f}")
elif test_case == "decoration_order_test":
# Uji bahwa urutan penambahan pelengkap tidak memengaruhi hasil akhir
decaf = Decaf()
order1 = Milk(Mocha(decaf))
order2 = Mocha(Milk(decaf))
print(f"Order 1 - {order1.get_description()}: ${order1.cost():.2f}")
print(f"Order 2 - {order2.get_description()}: ${order2.cost():.2f}")
print(f"Same cost: {order1.cost() == order2.cost()}")
elif test_case == "complex_order_test":
# Uji pesanan minuman yang kompleks
orders = [
Mocha(Whip(Espresso())),
Soy(Milk(HouseBlend())),
Whip(Mocha(Soy(DarkRoast()))),
Milk(Decaf())
]
for i, order in enumerate(orders, 1):
print(f"Order {i}: {order.get_description()}")
print(f"Cost: ${order.cost():.2f}")
print("---")
elif test_case == "double_condiment_test":
# Uji penambahan pelengkap yang sama beberapa kali
espresso = Espresso()
double_mocha = Mocha(Mocha(espresso))
print(f"Description: {double_mocha.get_description()}")
print(f"Cost: ${double_mocha.cost():.2f}")
elif test_case == "beverage_comparison_test":
# Bandingkan harga berbagai minuman dengan bahan tambahan yang sama
base_beverages = [Espresso(), HouseBlend(), DarkRoast(), Decaf()]
print("All beverages with Milk + Mocha:")
for beverage in base_beverages:
decorated = Milk(Mocha(beverage))
print(f"{decorated.get_description()}: ${decorated.cost():.2f}")
elif test_case == "condiment_cost_test":
# Uji harga setiap bahan tambahan
espresso = Espresso()
base_cost = espresso.cost()
condiments = [
("Milk", Milk(espresso)),
("Mocha", Mocha(espresso)),
("Soy", Soy(espresso)),
("Whip", Whip(espresso))
]
for name, decorated in condiments:
additional_cost = decorated.cost() - base_cost
print(f"{name} adds: ${additional_cost:.2f}")
elif test_case == "cheapest_most_expensive_test":
# Cari minuman dasar termurah dan termahal
beverages = [
("Espresso", Espresso()),
("House Blend", HouseBlend()),
("Dark Roast", DarkRoast()),
("Decaf", Decaf())
]
beverages.sort(key=lambda x: x[1].cost())
print(f"Cheapest: {beverages[0][0]} - ${beverages[0][1].cost():.2f}")
print(f"Most Expensive: {beverages[-1][0]} - ${beverages[-1][1].cost():.2f}")
elif test_case == "nested_decoration_test":
# Uji dekorasi yang bersarang dalam
beverage = HouseBlend()
# Tambahkan 5 lapisan dekorasi
decorated = Whip(Soy(Mocha(Milk(Whip(beverage)))))
print(f"Description: {decorated.get_description()}")
print(f"Cost: ${decorated.cost():.2f}")
# Hitung jumlah bahan tambahan
description = decorated.get_description()
condiment_count = description.count(" + ")
print(f"Number of condiments: {condiment_count}")
elif test_case == "cost_breakdown_test":
# Rincian harga secara terperinci
dark_roast = DarkRoast()
print(f"Base Dark Roast: ${dark_roast.cost():.2f}")
with_soy = Soy(dark_roast)
print(f"+ Soy: ${with_soy.cost():.2f}")
with_mocha = Mocha(with_soy)
print(f"+ Mocha: ${with_mocha.cost():.2f}")
with_whip = Whip(with_mocha)
print(f"+ Whip: ${with_whip.cost():.2f}")
final_cost = with_whip.cost()
print(f"Final total: ${final_cost:.2f}")
elif test_case == "description_format_test":
# Uji konsistensi format deskripsi
espresso = Espresso()
test_combinations = [
Milk(espresso),
Mocha(Milk(espresso)),
Whip(Mocha(Milk(espresso))),
Soy(Whip(Mocha(Milk(espresso))))
]
for combo in test_combinations:
description = combo.get_description()
print(f"Description: '{description}'")
# Periksa apakah deskripsi diawali nama minuman dasar
starts_correctly = description.startswith("Espresso")
print(f"Starts with base name: {starts_correctly}")
print("---")Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.
Semua pelajaran di Pemrograman Berorientasi Objek
1Dasar-Dasar OOP
File EksternalPengantar OOPclass vs objectParameter selfmethodAtributMethod Konstruktor (__init__)Rangkuman: Kalkulator Sederhana4Pewarisan
Pewarisan DasarFungsi super()Penimpaan MethodPewarisan BergandaUrutan Resolusi MethodRingkasan: Hierarki Karyawan5Polimorfisme
Meninjau Ulang Method OverridingDuck Typingclass dan method AbstrakDesain InterfaceRingkasan: Kalkulator Bentuk8Konsep OOP Lanjutan
Komposisi vs PewarisanMixinsMetode Statis dan Metode ClassDecorator ClassPengelola Konteks11Pola Desain Bagian 2
Pola PerintahPola AdapterPola DecoratorPola Template MethodPola StatePola Composite3Property class
Variabel instance vs variabel classDekorator propertyAtribut privatRangkuman: Pengelola Rekening Bank6Enkapsulasi
Anggota Public, Protected, PrivateModifier AksesPenyembunyian InformasiDecorator Property Tingkat LanjutUlasan: Sistem Catatan Siswa12Proyek: Manajemen Perpustakaan
Ikhtisar Proyekclass Buku dan PenggunaSistem PeminjamanFitur PencarianAntarmuka AdminPengujian dan IntegrasiLatihan sendiri: Compiler Python online