![Dekorator](/images/patterns/cards/decorator-mini.png?id=d30458908e315af195cb183bc52dbef9)
Dekorator w języku Ruby
Dekorator to strukturalny wzorzec pozwalający na dodawanie obiektom nowych obowiązków w sposób dynamiczny — poprzez “opakowywanie” ich w specjalne obiekty posiadające potrzebną funkcjonalność.
Stosując dekoratory można opakowywać obiekty wielokrotnie, gdyż zarówno obiekt docelowy jak i dekoratory są zgodne pod względem interfejsu. Wynikowy obiekt będzie posiadał ułożoną w formie stosu połączoną funkcjonalność wszystkich “opakowań”.
Złożoność:
Popularność:
Przykłady użycia: Dekorator jest dość typowy w kodzie Ruby, szczególnie tym dotyczącym strumieni.
Identyfikacja: Dekorator można poznać po metodach kreacyjnych lub konstruktorach przyjmujących obiekty tej samej klasy lub interfejsu jako bieżącą klasę.
Przykład koncepcyjny
Poniższy przykład ilustruje strukturę wzorca Dekorator ze szczególnym naciskiem na następujące kwestie:
- Z jakich składa się klas?
- Jakie role pełnią te klasy?
- W jaki sposób elementy wzorca są ze sobą powiązane?
main.rb: Przykład koncepcyjny
# The base Component interface defines operations that can be altered by
# decorators.
class Component
# @return [String]
def operation
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
# Concrete Components provide default implementations of the operations. There
# might be several variations of these classes.
class ConcreteComponent < Component
# @return [String]
def operation
'ConcreteComponent'
end
end
# The base Decorator class follows the same interface as the other components.
# The primary purpose of this class is to define the wrapping interface for all
# concrete decorators. The default implementation of the wrapping code might
# include a field for storing a wrapped component and the means to initialize
# it.
class Decorator < Component
attr_accessor :component
# @param [Component] component
def initialize(component)
@component = component
end
# The Decorator delegates all work to the wrapped component.
def operation
@component.operation
end
end
# Concrete Decorators call the wrapped object and alter its result in some way.
class ConcreteDecoratorA < Decorator
# Decorators may call parent implementation of the operation, instead of
# calling the wrapped object directly. This approach simplifies extension of
# decorator classes.
def operation
"ConcreteDecoratorA(#{@component.operation})"
end
end
# Decorators can execute their behavior either before or after the call to a
# wrapped object.
class ConcreteDecoratorB < Decorator
# @return [String]
def operation
"ConcreteDecoratorB(#{@component.operation})"
end
end
# The client code works with all objects using the Component interface. This way
# it can stay independent of the concrete classes of components it works with.
def client_code(component)
# ...
print "RESULT: #{component.operation}"
# ...
end
# This way the client code can support both simple components...
simple = ConcreteComponent.new
puts 'Client: I\'ve got a simple component:'
client_code(simple)
puts "\n\n"
# ...as well as decorated ones.
#
# Note how decorators can wrap not only simple components but the other
# decorators as well.
decorator1 = ConcreteDecoratorA.new(simple)
decorator2 = ConcreteDecoratorB.new(decorator1)
puts 'Client: Now I\'ve got a decorated component:'
client_code(decorator2)
output.txt: Wynik działania
Client: I've got a simple component:
RESULT: ConcreteComponent
Client: Now I've got a decorated component:
RESULT: ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))