Система автоматизированного проектирования
- 1 year ago
- 0
- 0
Декоратор ( англ. Decorator) — структурный шаблон проектирования , предназначенный для динамического подключения дополнительного поведения к объекту . Шаблон Декоратор предоставляет гибкую альтернативу практике создания подклассов с целью расширения функциональности.
Объект, который предполагается использовать, выполняет основные функции. Однако может потребоваться добавить к нему некоторую дополнительную функциональность, которая будет выполняться до, после или даже вместо основной функциональности объекта.
Декоратор предусматривает расширение функциональности объекта без определения подклассов.
Класс
ConcreteComponent
— класс, в который с помощью шаблона Декоратор добавляется новая функциональность. В некоторых случаях базовая функциональность предоставляется классами, производными от класса
ConcreteComponent
. В подобных случаях класс
ConcreteComponent
является уже не конкретным, а
абстрактным
. Абстрактный класс
Component
определяет
интерфейс
для использования всех этих классов.
ConcreteComponent
.
Создаётся абстрактный класс, представляющий как исходный класс, так и новые, добавляемые в класс функции. В классах-декораторах новые функции вызываются в требуемой последовательности — до или после вызова последующего объекта.
При желании остаётся возможность использовать исходный класс (без расширения функциональности), если на его объект сохранилась ссылка.
ConcreteComponent
.
Драйверы-фильтры в ядре Windows (архитектура WDM (Windows Driver Model) ) представляют собой декораторы. Несмотря на то, что WDM реализована на не-объектном языке Си , в ней чётко прослеживаются паттерны проектирования — декоратор, цепочка обязанностей , и команда (объект IRP ).
Архитектура COM (Component Object Model) не поддерживает наследование реализаций, вместо него предлагается использовать декораторы (в данной архитектуре это называется «агрегация»). При этом архитектура решает (с помощью механизма pUnkOuter) проблему object identity, возникающую при использовании декораторов — identity агрегата есть identity его самого внешнего декоратора.
fun main() { LoggingNotifier(FancyNotifier(ConsoleNotifier())).notify("Hello, World!") } interface Notifier { fun notify(message: String) } class ConsoleNotifier : Notifier { override fun notify(message: String) { println(message) } } class LoggingNotifier(private val notifier: Notifier) : Notifier { override fun notify(message: String) { notifier.notify(message) println("LOG - $message") // Like a logger } } class FancyNotifier(private val notifier: Notifier) : Notifier { override fun notify(message: String) { val border = "-".repeat(message.length) notifier.notify(""" $border $message $border """.trimIndent()) } }
module DecoratorPattern # Extends basic functionallity combining several Decorators class Source def initialize(line) @line = line end def write_line @line end end # Abstract Decorator module Decorator def initialize(source) @source = source end def write_line raise NotImplementedError end end # Concrete Decorator class Upcaser include Decorator def write_line @source.write_line.upcase end end # Concrete Decorator class Timestamper include Decorator def write_line "#{Time.now.strftime('%H:%m')} #{@source.write_line}" end end # Concrete Decorator class Datestamper include Decorator def write_line "#{Time.now.strftime('%d.%m.%y')} #{@source.write_line}" end end def self.run puts '=> Decorator' source = Source.new('Lorem ipsum dolor sit amet') puts "Source:\n=> #{source.write_line}" upcased = Upcaser.new(source) puts "Upcased:\n=> #{upcased.write_line}" timestamped = Timestamper.new(source) puts "Timestamped:\n=> #{timestamped.write_line}" datestamped = Datestamper.new(source) puts "Datestamped:\n=> #{datestamped.write_line}" upcased_timestamped = Timestamper.new(Upcaser.new(source)) puts "Upcased and timestamped:\n=> #{upcased_timestamped.write_line}" upcased_datestamped_timestamped = Datestamper.new(Timestamper.new(Upcaser.new(source))) puts "Upcased, datestamped and timestamped:\n=> #{upcased_datestamped_timestamped.write_line}" datestamped_timestamped = Datestamper.new(Timestamper.new(source)) puts "Datestamped and timestamped:\n=> #{datestamped_timestamped.write_line}" puts '' end end DecoratorPattern.run # => Decorator # Source: # => Lorem ipsum dolor sit amet # Upcased: # => LOREM IPSUM DOLOR SIT AMET # Timestamped: # => 18:03 Lorem ipsum dolor sit amet # Datestamped: # => 29.03.19 Lorem ipsum dolor sit amet # Upcased and timestamped: # => 18:03 LOREM IPSUM DOLOR SIT AMET # Upcased, datestamped and timestamped: # => 29.03.19 18:03 LOREM IPSUM DOLOR SIT AMET # Datestamped and timestamped: # => 29.03.19 18:03 Lorem ipsum dolor sit amet
public interface InterfaceComponent { void doOperation(); } class MainComponent implements InterfaceComponent { @Override public void doOperation() { System.out.print("World!"); } } abstract class Decorator implements InterfaceComponent { protected InterfaceComponent component; public Decorator (InterfaceComponent c) { component = c; } @Override public void doOperation() { component.doOperation(); } public void newOperation() { System.out.println("Do Nothing"); } } class DecoratorSpace extends Decorator { public DecoratorSpace(InterfaceComponent c) { super(c); } @Override public void doOperation() { System.out.print(" "); super.doOperation(); } @Override public void newOperation() { System.out.println("New space operation"); } } class DecoratorComma extends Decorator { public DecoratorComma(InterfaceComponent c) { super(c); } @Override public void doOperation() { System.out.print(","); super.doOperation(); } @Override public void newOperation() { System.out.println("New comma operation"); } } class DecoratorHello extends Decorator { public DecoratorHello(InterfaceComponent c) { super(c); } @Override public void doOperation() { System.out.print("Hello"); super.doOperation(); } @Override public void newOperation() { System.out.println("New hello operation"); } } class Main { public static void main (String... s) { Decorator c = new DecoratorHello(new DecoratorComma(new DecoratorSpace(new MainComponent()))); c.doOperation(); // Результат выполнения программы "Hello, World!" c.newOperation(); // New hello operation } }
using System; namespace Decorator { class MainApp { static void Main() { // Create ConcreteComponent and two Decorators ConcreteComponent c = new ConcreteComponent(); ConcreteDecoratorA dA = new ConcreteDecoratorA(); ConcreteDecoratorB dB = new ConcreteDecoratorB(); // Link decorators dA.SetComponent(c); dB.SetComponent(dA); dA.Operation(); Console.WriteLine(); dB.Operation(); // Wait for user Console.Read(); } } /// <summary> /// Component - компонент /// </summary> /// <remarks> /// <li> /// <lu>определяем интерфейс для объектов, на которые могут быть динамически /// возложены дополнительные обязанности;</lu> /// </li> /// </remarks> abstract class Component { public abstract void Operation(); } /// <summary> /// ConcreteComponent - конкретный компонент /// </summary> /// <remarks> /// <li> /// <lu>определяет объект, на который возлагается дополнительные обязанности</lu> /// </li> /// </remarks> class ConcreteComponent : Component { public override void Operation() { Console.Write("Привет"); } } /// <summary> /// Decorator - декоратор /// </summary> /// <remarks> /// <li> /// <lu>хранит ссылку на объект <see cref="Component"/> и определяет интерфейс, /// соответствующий интерфейсу <see cref="Component"/></lu> /// </li> /// </remarks> abstract class Decorator : Component { protected Component component; public void SetComponent(Component component) { this.component = component; } public override void Operation() { if (component != null) { component.Operation(); } } } /// <summary> /// ConcreteDecoratorA - конкретный декоратор /// </summary> /// <remarks> /// <li> /// <lu>Выполняет основную задачу</lu> /// </li> /// </remarks> class ConcreteDecoratorA : Decorator { public override void Operation() { base.Operation(); } } /// <summary> /// ConcreteDecorator - конкретный декоратор /// </summary> /// <remarks> /// <li> /// <lu>Выполняет основную задачу + дополнительную</lu> /// </li> /// </remarks> class ConcreteDecoratorB : Decorator { public override void Operation() { base.Operation(); Console.Write(" Мир!"); } } }
#include <iostream> #include <memory> class IComponent { public: virtual void operation() = 0; virtual ~IComponent(){} }; class Component : public IComponent { public: virtual void operation() { std::cout<<"World!"<<std::endl; } }; class DecoratorOne : public IComponent { std::shared_ptr<IComponent> m_component; public: DecoratorOne(std::shared_ptr<IComponent> component): m_component(component) {} virtual void operation() { std::cout << ", "; m_component->operation(); } }; class DecoratorTwo : public IComponent { std::shared_ptr<IComponent> m_component; public: DecoratorTwo(std::shared_ptr<IComponent> component): m_component(component) {} virtual void operation() { std::cout << "Hello"; m_component->operation(); } }; int main() { DecoratorTwo obj(std::make_shared<DecoratorOne>(std::make_shared<Component>())); obj.operation(); // prints "Hello, World!\n" return 0; }
import std.stdio; abstract class Figure { protected string name; string getInfo(); } class Empty : Figure { override string getInfo() { return null; } } class Circle : Figure { protected Figure figure; this(Figure f) { figure = f; name = " circle "; } override string getInfo() { return name ~ figure.getInfo(); } } class Bar : Figure { protected Figure figure; this(Figure f) { figure = f; name = " bar "; } override string getInfo() { return figure.getInfo() ~ name; } } void main() { Figure figures = new Bar(new Circle(new Bar(new Circle(new Empty())))); writeln(figures.getInfo()); }
Ниже — пример реализации шаблона проектирования. В Python существуют декораторы функций и классов , концепция которых отличается от концепции шаблона проектирования.
""" Demonstrated decorators in a world of a 10x10 grid of values 0-255. """ import random def s32_to_u16(x): if x < 0: sign = 0xf000 else: sign = 0 bottom = x & 0x00007fff return bottom | sign def seed_from_xy(x,y): return s32_to_u16(x) | (s32_to_u16(y) << 16) class RandomSquare: def __init__(s, seed_modifier): s.seed_modifier = seed_modifier def get(s, x,y): seed = seed_from_xy(x,y) ^ s.seed_modifier random.seed(seed) return random.randint(0,255) class DataSquare: def __init__(s, initial_value = None): s.data = [initial_value]*10*10 def get(s, x,y): return s.data[ (y*10)+x ] # yes: these are all 10x10 def set(s, x,y, u): s.data[ (y*10)+x ] = u class CacheDecorator: def __init__(s, decorated): s.decorated = decorated s.cache = DataSquare() def get(s, x,y): if s.cache.get(x,y) == None: s.cache.set(x,y, s.decorated.get(x,y)) return s.cache.get(x,y) class MaxDecorator: def __init__(s, decorated, max): s.decorated = decorated s.max = max def get(s, x,y): if s.decorated.get(x,y) > s.max: return s.max return s.decorated.get(x,y) class MinDecorator: def __init__(s, decorated, min): s.decorated = decorated s.min = min def get(s, x,y): if s.decorated.get(x,y) < s.min: return s.min return s.decorated.get(x,y) class VisibilityDecorator: def __init__(s, decorated): s.decorated = decorated def get(s,x,y): return s.decorated.get(x,y) def draw(s): for y in range(10): for x in range(10): print "%3d" % s.get(x,y), print # Now, build up a pipeline of decorators: random_square = RandomSquare(635) random_cache = CacheDecorator(random_square) max_filtered = MaxDecorator(random_cache, 200) min_filtered = MinDecorator(max_filtered, 100) final = VisibilityDecorator(min_filtered) final.draw()
Выходные данные (учтите использование генератора псевдослучайных чисел):
100 100 100 100 181 161 125 100 200 100 200 100 100 200 100 200 200 184 162 100 155 100 200 100 200 200 100 200 143 100 100 200 144 200 101 143 114 200 166 136 100 147 200 200 100 100 200 141 172 100 144 161 100 200 200 200 190 125 100 177 150 200 100 175 111 195 193 128 100 100 100 200 100 200 200 129 159 105 112 100 100 101 200 200 100 100 200 100 101 120 180 200 100 100 198 151 100 195 131 100
abstract class AbstractComponent { abstract public function operation(); } class ConcreteComponent extends AbstractComponent { public function operation() { // ... } } abstract class AbstractDecorator extends AbstractComponent { protected $component; public function __construct(AbstractComponent $component) { $this->component = $component; } } class ConcreteDecorator extends AbstractDecorator { public function operation() { // ... расширенная функциональность ... $this->component->operation(); // ... расширенная функциональность ... } } $decoratedComponent = new ConcreteDecorator(new ConcreteComponent()); $decoratedComponent->operation();
<?php interface IText { public function show(); } class TextHello implements IText { protected $object; public function __construct(IText $text) { $this->object = $text; } public function show() { echo 'Hello'; $this->object->show(); } } class TextWorld implements IText { protected $object; public function __construct(IText $text) { $this->object = $text; } public function show() { echo 'world'; $this->object->show(); } } class TextSpace implements IText { protected $object; public function __construct(IText $text) { $this->object = $text; } public function show() { echo ' '; $this->object->show(); } } class TextEmpty implements IText { public function show() { } } $decorator = new TextHello(new TextSpace(new TextWorld(new TextEmpty()))); $decorator->show(); // Hello world echo '<br />' . PHP_EOL; $decorator = new TextWorld(new TextSpace(new TextHello(new TextEmpty()))); $decorator->show(); // world Hello
# Компонент class Notebook # Маркетинг price : 500 # $ # Характеристики hdd : 320 # GB ram : 4 # GB core : 'i5 2.3' # GHz # Декоратор class NovaNotebook constructor : (product) -> @price = product.price * 1.3 # Декоратор class ImportNotebook constructor : (product) -> @price = product.price * 1.5 # Декоратор class AppleNotebook constructor : (product) -> @price = product.price * 2.1 macBookInRussia = new ImportNotebook new NovaNotebook new AppleNotebook new Notebook console.log(macBookInRussia.price)
Шаблон декоратор в языках с динамической типизацией может быть применён без интерфейсов и традиционного для ООП наследования.
Этот пример скопирован с английской версии статьи. Расчёт стоимости кофе:
// ConcreteComponent (класс для последующего декорирования) function Coffee() { this.cost = function() { return 1; }; } // Decorator A function Milk(coffee) { this.cost = function() { return coffee.cost() + 0.5; }; } // Decorator B function Whip(coffee) { this.cost = function() { return coffee.cost() + 0.7; }; } // Decorator C function Sprinkles(coffee) { this.cost = function() { return coffee.cost() + 0.2; }; } // Можно использовать, например, так: var coffee = new Milk(new Whip(new Sprinkles(new Coffee()))); alert(coffee.cost()); // Или более наглядно: var coffee = new Coffee(); coffee = new Sprinkles(coffee); coffee = new Whip(coffee); coffee = new Milk(coffee); alert(coffee.cost());
Реализация имеющегося выше C# примера. В ConcreteComponent добавлена локальная переменная price, которая будет изменяться как в нём самом, так и декораторах. Имена классов (кроме постфиксов "A" и "B") совпадают с именами участников шаблона.
function Component() { this.operation = function() { }; this.getPrice = function() { }; this.setPrice = function() { }; } function ConcreteComponent() { var price = 10; this.operation = function() { price += 4; alert("ConcreteComponent.operation, price: "+ price); }; this.getPrice = function() { return price; }; this.setPrice = function(val) { price = val; }; } ConcreteComponent.prototype = new Component(); ConcreteComponent.prototype.constructor = ConcreteComponent; function Decorator() { var component; this.setComponent = function(val) { component = val; }; this.getComponent = function() { return component; }; this.operation = function() { component.operation(); }; this.getPrice = function() { return component.getPrice(); }; this.setPrice = function(val) { component.setPrice(val); }; } Decorator.prototype = new Component(); Decorator.prototype.constructor = Decorator; function ConcreteDecoratorA() { Decorator.call(this); var operation = this.operation; // ссылка на метод, определенный в Decorator this.operation = function() { this.setPrice(this.getPrice() + 3); alert("ConcreteDecoratorA.operation, price: "+ this.getPrice()); operation(); }; } function ConcreteDecoratorB() { var dublicate = this; // ссылка на инстанцирующийся объект (т.к. this может меняться) Decorator.call(this); var operation = this.operation; // ссылка на метод, определенный в Decorator this.operation = function() { this.setPrice(this.getPrice() + 1); alert("ConcreteDecoratorB.operation, price: "+ this.getPrice()); addedBehavior(); operation(); }; function addedBehavior() { dublicate.setPrice(dublicate.getPrice() + 2); alert("addedBehavior, price: "+ dublicate.getPrice()); } } // использование c = new ConcreteComponent(); d1 = new ConcreteDecoratorA(); d2 = new ConcreteDecoratorB(); alert("изначальная цена: " + c.getPrice()); // 10 d1.setComponent(c); d2.setComponent(d1); d2.operation(); alert("цена после преобразования: " + c.getPrice()); // 20
Namespace Decorator Class Program Shared Sub Main() ' Создание ConcreteComponent и двух декораторов Dim C As New ConcreteComponent() Dim D1 As New ConcreteDecoratorA() Dim D2 As New ConcreteDecoratorB() ' Ссылки декоратора D1.SetComponent(C) D2.SetComponent(D1) D2.Operation() ' Ожидание действий от пользователя Console.Read() End Sub End Class ''' <summary> ''' Component - компонент ''' </summary> ''' <remarks> ''' <li> ''' <lu>определяем интерфейс для объектов, на которые могут быть динамически ''' возложены дополнительные обязанности;</lu> ''' </li> ''' </remarks> MustInherit Class Component Public MustOverride Sub Operation() End Class ''' <summary> ''' ConcreteComponent - конкретный компонент ''' </summary> ''' <remarks> ''' <li> ''' <lu>определяет объект, на который возлагается дополнительные обязанности</lu> ''' </li> ''' </remarks> Class ConcreteComponent Inherits Component Public Overrides Sub Operation() Console.WriteLine("ConcreteComponent.Operation()") End Sub End Class ''' <summary> ''' Decorator - декоратор ''' </summary> ''' <remarks> ''' <li> ''' <lu>хранит ссылку на объект <see cref="Component"/> и определяет интерфейс, ''' соответствующий интерфейсу <see cref="Component"/></lu> ''' </li> ''' </remarks> MustInherit Class Decorator Inherits Component Protected component As Component Public Sub SetComponent(ByVal component As Component) Me.component = component End Sub Public Overrides Sub Operation() If component IsNot Nothing Then component.Operation() End If End Sub End Class ''' <summary> ''' ConcreteDecorator - конкретный декоратор ''' </summary> ''' <remarks> ''' <li> ''' <lu>возглагает дополнительные обязанности на компонент.</lu> ''' </li> ''' </remarks> Class ConcreteDecoratorA Inherits Decorator Private addedState As String Public Overrides Sub Operation() MyBase.Operation() addedState = "New State" Console.WriteLine("ConcreteDecoratorA.Operation()") End Sub End Class ' "ConcreteDecoratorB" Class ConcreteDecoratorB Inherits Decorator Public Overrides Sub Operation() MyBase.Operation() AddedBehavior() Console.WriteLine("ConcreteDecoratorB.Operation()") End Sub Private Sub AddedBehavior() End Sub End Class End Namespace
Языки Delphi и Free Pascal поддерживают class helpers, которые делают ненужным использование шаблона декоратор .
program NoMoreDecorators; type TMyObject = class procedure WriteHello; end; TMyObjectHelper = class helper for TMyObject procedure WriteHello(const Name: string); overload; end; procedure TMyObject.WriteHello; begin writeln('Hello'); end; procedure TMyObjectHelper.WriteHello(const Name: string); begin writeln('Hello, ', Name, '!'); end; var o: TMyObject; begin o := TMyObject.Create; o.WriteHello; o.WriteHello('Jean'); o.Free; end.
program DecoratorPattern; {$APPTYPE CONSOLE} uses SysUtils; type TInterfaceComponent = class public procedure Operation; virtual; abstract; end; type TConcreteComponent = class(TInterfaceComponent) public procedure Operation; override; end; procedure TConcreteComponent.Operation; begin Write('нельзя'); end; type TDecorator = class(TInterfaceComponent) private FComponent: TInterfaceComponent; public constructor Create(aComponent: TInterfaceComponent); end; constructor TDecorator.Create(aComponent: TInterfaceComponent); begin FComponent := aComponent; end; type TBeforeDecorator = class(TDecorator) public procedure Operation; override; end; procedure TBeforeDecorator.Operation; begin Write('Казнить, '); FComponent.Operation; end; type TAfterDecorator = class(TDecorator) public procedure Operation; override; end; procedure TAfterDecorator.Operation; begin FComponent.Operation; Write(' помиловать'); end; type TOverrideDecorator = class(TDecorator) public procedure Operation; override; end; procedure TOverrideDecorator.Operation; begin Write('Любите друг друга!'); end; var vSameComponent: TInterfaceComponent; begin vSameComponent := TAfterDecorator.Create(TConcreteComponent.Create); vSameComponent.Operation; // Будет выведено "нельзя помиловать" Writeln; vSameComponent := TBeforeDecorator.Create(vSameComponent); vSameComponent.Operation; // Будет выведено "Казнить, нельзя помиловать" Writeln; vSameComponent := TOverrideDecorator.Create(vSameComponent); vSameComponent.Operation; // Будет выведено "Любите друг друга!" // Ради упрощения примера уничтожение объектов не показано ReadLn; end.
protocol Book { var title: String { get set } var price: Int { get set } func getPrice() -> Int } class BookImpl: Book { var title: String = "" var price: Int = 1000 func getPrice() -> Int { return price } } class DiscountBook: Book { let element: BookImpl var title: String = "«Грокаем алгоритмы»" var price: Int = 0 init(element: BookImpl) { self.element = element self.title = element.title self.price = element.price } // 30% sale func getPrice() -> Int { return price - (price * 30)/100 } } // Use Decorator let book = BookImpl() let discountBook = DiscountBook(element: book) print(discountBook.getPrice())