Menu
Coddy logo textTech

Dekoratör Kalıbı

Coddy'nin Python Journey'sinin Object Oriented Programming bölümünün bir parçası — ders 51 / 64.

Decorator Deseni, nesnelerin yapılarını değiştirmeden onlara dinamik olarak yeni işlevsellikler ekler. Davranışlarını genişletmek için nesneleri sarmalar.

İşte basit bir kahve örneği:

class Coffee:
    def cost(self):
        return 5
    
    def description(self):
        return "Simple coffee"

Kahveye özellikler ekleyen dekoratörler oluşturun:

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"

Her bir dekoratör başka bir nesneyi sarmalar ve kendi işlevselliğini ekler.

Özelleştirilmiş kahve oluşturmak için dekoratörleri kullanın:

# Temel kahve ile başla
my_coffee = Coffee()
print(f"{my_coffee.description()}: ${my_coffee.cost()}")

# Süt ekle
my_coffee = MilkDecorator(my_coffee)
print(f"{my_coffee.description()}: ${my_coffee.cost()}")

# Şeker ekle
my_coffee = SugarDecorator(my_coffee)
print(f"{my_coffee.description()}: ${my_coffee.cost()}")

Metin biçimlendirmeli başka bir örnek oluşturun:

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>"

# Biçimlendirilmiş metni adım adım oluşturun
message = Text("Hello World")
message = BoldDecorator(message)
message = ItalicDecorator(message)

print(message.render())

Çıktı:

Simple coffee: $5
Simple coffee + Milk: $7
Simple coffee + Milk + Sugar: $8
<i><b>Hello World</b></i>

Anahtar Nokta: Decorator Deseni, orijinal nesneyi değiştirmeden yeni davranışlar eklemek için nesneleri sarmalar. Her dekoratör, sarmalanmış nesneye bir referans tutar ve kendi işlevselliğini ekler. Bu, birçok alt sınıf oluşturmadan esnek özellik kombinasyonları için birden fazla dekoratörü birleştirmenize olanak tanır.

challenge icon

Görev

Orta

Bu meydan okumada, bir kahve dükkanı için esnek bir kahve sipariş sistemi oluşturmak amacıyla Decorator tasarım desenini uygulayacaksınız. Decorator deseni, aynı sınıftan diğer nesnelerin davranışını etkilemeden, tekil nesnelere dinamik olarak davranış eklenmesine olanak tanır.

Bir kahve dükkanı için şu özelliklere sahip bir sistem oluşturuyorsunuz:

  • Çeşitli içecek türleri sunmak (Espresso, Dark Roast, House Blend, Decaf)
  • Müşterilerin içeceklerine çeşniler (Milk, Mocha, Soy, Whipped Cream) eklemesine izin vermek
  • Eklenen çeşnilerle birlikte içeceklerin maliyetini hesaplamak
  1. beverage.py dosyasındaki soyut Beverage sınıfını ve onun somut uygulamalarını tamamlayın
  2. condiment_decorator.py dosyasındaki CondimentDecorator soyut sınıfını ve somut dekoratörleri uygulayın
  3. Uygulamanızı doğrulamak için driver.py dosyasında test senaryolarını uygulayın

Kopya kağıdı

Decorator (Dekoratör) Deseni, nesneleri davranışlarını genişletmek için sarmalayarak, yapılarını değiştirmeden nesnelere dinamik olarak yeni işlevsellikler ekler.

Bir kahve örneği ile temel yapı:

class Coffee:
    def cost(self):
        return 5
    
    def description(self):
        return "Simple coffee"

İşlevselliği sarmalayan ve genişleten dekoratörler oluşturun:

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"

Özelleştirilmiş nesneler oluşturmak için dekoratörleri zincirleyin:

# Temel kahve ile başlayın
my_coffee = Coffee()

# Dekoratörleri ekleyin
my_coffee = MilkDecorator(my_coffee)
my_coffee = SugarDecorator(my_coffee)

print(f"{my_coffee.description()}: ${my_coffee.cost()}")
# Çıktı: Simple coffee + Milk + Sugar: $8

Metin biçimlendirme örneği:

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>"

# Dekoratörleri zincirleyin
message = Text("Hello World")
message = BoldDecorator(message)
message = ItalicDecorator(message)

print(message.render())
# Çıktı: <i><b>Hello World</b></i>

Temel avantajlar: Çok sayıda alt sınıf oluşturmadan esnek özellik kombinasyonlarına izin verir, sarmalanmış nesneye referansı korur ve işlevselliği dinamik olarak ekler.

Kendin dene

# Gerekli tüm sınıfları içe aktar
from beverage import Espresso, HouseBlend, DarkRoast, Decaf
from condiment_decorator import Milk, Mocha, Soy, Whip

# Kapsamlı test senaryosu işleyicisi
test_case = input()

if test_case == "basic_beverage_test":
    # Temel içecek işlevselliğini test et
    espresso = Espresso()
    print(f"Description: {espresso.get_description()}")
    print(f"Cost: ${espresso.cost():.2f}")

elif test_case == "all_beverages_test":
    # Tüm içecek türlerini test et
    beverages = [Espresso(), HouseBlend(), DarkRoast(), Decaf()]
    for beverage in beverages:
        print(f"{beverage.get_description()}: ${beverage.cost():.2f}")

elif test_case == "single_condiment_test":
    # Tekli garnitür süslemesini test et
    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":
    # Çoklu garnitür süslemelerini test et
    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":
    # Bir içecek üzerindeki tüm garnitürleri test et
    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":
    # Farklı kombinasyonlar için maliyet hesaplamalarını test et
    espresso = Espresso()
    
    # Tekli eklemeler
    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":
    # Süsleme sırasının nihai sonucu etkilemediğini test et
    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":
    # Karmaşık içecek siparişlerini test et
    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":
    # Aynı garnitürün birden fazla kez eklenmesini test et
    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":
    # Aynı garnitürlere sahip farklı içeceklerin maliyetlerini karşılaştır
    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":
    # Bireysel garnitür maliyetlerini test et
    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":
    # En ucuz ve en pahalı temel içecekleri bul
    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":
    # Derinlemesine iç içe geçmiş süslemeleri test et
    beverage = HouseBlend()
    # 5 katman süsleme ekle
    decorated = Whip(Soy(Mocha(Milk(Whip(beverage)))))
    print(f"Description: {decorated.get_description()}")
    print(f"Cost: ${decorated.cost():.2f}")
    
    # Garnitür sayısını say
    description = decorated.get_description()
    condiment_count = description.count(" + ")
    print(f"Number of condiments: {condiment_count}")

elif test_case == "cost_breakdown_test":
    # Detaylı maliyet dökümü
    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":
    # Açıklama biçimlendirme tutarlılığını test et
    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}'")
        # Açıklamanın temel içecek adıyla başlayıp başlamadığını kontrol et
        starts_correctly = description.startswith("Espresso")
        print(f"Starts with base name: {starts_correctly}")
        print("---")
quiz iconKendini test et

Bu ders kısa bir quiz içerir. Soruları yanıtlamak ve ilerlemeni kaydetmek için derse başla.

Object Oriented Programming bölümündeki tüm dersler