Wzorce projektowe w Pythonie - dekorator

Wzorzec dekoratora umożliwia użytkownikowi dodanie nowej funkcjonalności do istniejącego obiektu bez zmiany jego struktury. Ten typ wzorca projektowego jest objęty wzorem strukturalnym, ponieważ ten wzorzec działa jak opakowanie dla istniejącej klasy.

Ten wzorzec tworzy klasę dekoratora, która otacza klasę oryginalną i zapewnia dodatkową funkcjonalność, utrzymując sygnaturę metod klasy w stanie nienaruszonym.

Motywem wzoru dekoratora jest dynamiczne przywiązanie do obiektu dodatkowych obowiązków.

Jak wdrożyć wzorzec projektowy dekoratora

Kod wymieniony poniżej jest prostą demonstracją, jak zaimplementować wzorzec projektowy dekoratora w Pythonie. Ilustracja przedstawia kawiarnię w formacie klasowym. Utworzona klasa kawy jest abstrakcją, co oznacza, że ​​nie można jej utworzyć instancji.

import six
from abc import ABCMeta

@six.add_metaclass(ABCMeta)
class Abstract_Coffee(object):

   def get_cost(self):
      pass

   def get_ingredients(self):
      pass
   
   def get_tax(self):
      return 0.1*self.get_cost()

class Concrete_Coffee(Abstract_Coffee):
   
   def get_cost(self):
      return 1.00
   
   def get_ingredients(self):
      return 'coffee'

@six.add_metaclass(ABCMeta)
class Abstract_Coffee_Decorator(Abstract_Coffee):
   
   def __init__(self,decorated_coffee):
      self.decorated_coffee = decorated_coffee
   
   def get_cost(self):
      return self.decorated_coffee.get_cost()
   
   def get_ingredients(self):
      return self.decorated_coffee.get_ingredients()

class Sugar(Abstract_Coffee_Decorator):
   
   def __init__(self,decorated_coffee):
      Abstract_Coffee_Decorator.__init__(self,decorated_coffee)
   
   def get_cost(self):
      return self.decorated_coffee.get_cost()
   
   def get_ingredients(self):
	   return self.decorated_coffee.get_ingredients() + ', sugar'

class Milk(Abstract_Coffee_Decorator):
   
   def __init__(self,decorated_coffee):
      Abstract_Coffee_Decorator.__init__(self,decorated_coffee)
   
   def get_cost(self):
      return self.decorated_coffee.get_cost() + 0.25
   
   def get_ingredients(self):
      return self.decorated_coffee.get_ingredients() + ', milk'

class Vanilla(Abstract_Coffee_Decorator):
   
   def __init__(self,decorated_coffee):
      Abstract_Coffee_Decorator.__init__(self,decorated_coffee)
   
   def get_cost(self):
      return self.decorated_coffee.get_cost() + 0.75
   
   def get_ingredients(self):
      return self.decorated_coffee.get_ingredients() + ', vanilla'

Implementacja abstrakcyjnej klasy kawiarni odbywa się za pomocą osobnego pliku, jak wspomniano poniżej -

import coffeeshop

myCoffee = coffeeshop.Concrete_Coffee()
print('Ingredients: '+myCoffee.get_ingredients()+
   '; Cost: '+str(myCoffee.get_cost())+'; sales tax = '+str(myCoffee.get_tax()))

myCoffee = coffeeshop.Milk(myCoffee)
print('Ingredients: '+myCoffee.get_ingredients()+
   '; Cost: '+str(myCoffee.get_cost())+'; sales tax = '+str(myCoffee.get_tax()))

myCoffee = coffeeshop.Vanilla(myCoffee)
print('Ingredients: '+myCoffee.get_ingredients()+
   '; Cost: '+str(myCoffee.get_cost())+'; sales tax = '+str(myCoffee.get_tax()))

myCoffee = coffeeshop.Sugar(myCoffee)
print('Ingredients: '+myCoffee.get_ingredients()+
   '; Cost: '+str(myCoffee.get_cost())+'; sales tax = '+str(myCoffee.get_tax()))

Wynik

Powyższy program generuje następujące dane wyjściowe -