旧游无处不堪寻
无寻处,惟有少年心
设计模式-装饰器

装饰器模式(Decorator Pattern)是一种结构型设计模式,允许你通过将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为。

解释


当你需要更改一个对象的行为时,第一个想法就是继承所属的类。但是,继承可能引发的几个严重问题:

  1. 继承是静态的。你无法在运行时更改已有对象的行为,只能使用由不同子类创建的对象来替代当前的整个对象
  2. 子类只能有一个父类。大部分编程语言不允许一个类同时继承多个类的行为

另一种方法是用聚合或组合,而不是继承。
一个对象包含指向另一个对象的引用,并将部分工作委派给引用对象。你可以使用这个方式在运行时改变容器的行为。

聚合: 对象 A 包含对象 B。B 可以独立于 A 存在。
组合: 对象 A 由对象 B 构成。A 负责管理 B 的生命周期。B 无法独立于 A 存在。
注意: 聚合(或组合)是许多设计模式背后的关键原则

装饰器模式 UML


适用场景


  1. 如果你希望在无需修改代码的情况下即可使用对象,且希望在运行时为对象新增额外的行为,可以使用装饰器模式
  2. 如果用继承来扩展对象行为的方案难以实现或者根本不可行,例如许多编程语言使用 final 最终关键字来限制对某个类的进一步扩展。复用最终类已有行为的唯一方法是使用装饰器模式

代码示例


using System;
namespace DecoratorPatternDemo
{
public abstract class Component
{
public abstract string Operation();
}

class ConcreteComponent : Component
{
public override string Operation()
{
return "ConcreteComponent";
}
}

abstract class Decorator : Component
{
protected Component _component;

public Decorator(Component component)
{
this._component = component;
}

public void SetComponent(Component component)
{
this._component = component;
}

public override string Operation()
{
if (this._component != null)
{
return this._component.Operation();
}
else
{
return string.Empty;
}
}
}

class ConcreteDecoratorA : Decorator
{
public ConcreteDecoratorA(Component comp) : base(comp)
{
}

public override string Operation()
{
return $"ConcreteDecoratorA({base.Operation()})";
}
}

class ConcreteDecoratorB : Decorator
{
public ConcreteDecoratorB(Component comp) : base(comp)
{
}

public override string Operation()
{
return $"ConcreteDecoratorB({base.Operation()})";
}
}

public class Client
{
public void ClientCode(Component component)
{
Console.WriteLine("RESULT: " + component.Operation());
}
}

class Program
{
static void Main(string[] args)
{
Client client = new Client();

var simple = new ConcreteComponent();
Console.WriteLine("Client: I get a simple component:");
client.ClientCode(simple);
Console.WriteLine();

ConcreteDecoratorA decorator1 = new ConcreteDecoratorA(simple);
ConcreteDecoratorB decorator2 = new ConcreteDecoratorB(decorator1);
Console.WriteLine("Client: Now I've got a decorated component:");
client.ClientCode(decorator2);
// output

// Client: I get a simple component:
// RESULT: ConcreteComponent

// Client: Now I've got a decorated component:
// RESULT: ConcreteDecoratorB(ConcreteDecoratorA(ConcreteComponent))
}
}
}