前置文章: 设计模式的原则
其他设计模式:用心理解设计模式专栏
设计模式相关代码已统一放至 我的 Github
一、定义
结构型模式之一。
Attach additional responsibilites to an object dynamically keeping the same interface. Decorators provide a flexible alternative to subclassing for extending functionality.
(动态地为一个对象添加一些额外的职责并保持接口不变。装饰器为扩展功能提供了一个灵活的替代子类的方法。)
二、结构解析
装饰器模式的一般结构有四种角色: 抽象组件、具体组件、抽象装饰器、具体装饰器。
抽象组件(Component):负责定义具体组件要被装饰的业务方法接口。
具体组件(ConcretComponent):被装饰者,继承抽象组件,实现自己原有的方法。
抽象装饰器(Decorator ):继承抽象组件,持有(聚合)一个抽象组件类型的成员,实际可接收一个具体组件或另一个装饰器。可将继承自抽象组件的接口方法定义为 模板方法(即,约定装饰规则),然后让子类去实现具体的装饰逻辑(下面示例就是这样做的)。或者,也可给出空的或默认的装饰实现,由子类选择性的重写。最后,不管怎么做,装饰器实现的接口方法,都要最终调用持有的成员实现的该接口方法(只装饰,不完全重写)。
具体装饰器(ConcreteDecorator):负责实现具体的装饰逻辑,发挥实际的装饰作用,派生不同的具体装饰器可以产生不同的装饰效果。
三、评价
装饰器模式,具有如继承一样,扩展某个类职责的功能。 但它与继承不同的是,继承是通过扩展出子类特有接口的方式来扩展职责,装饰器是以增强原接口功能的方式(保持接口不变,扩展的职责被原接口调用)来扩展类的职责。
装饰器模式在特定情况下,比继承更简洁、更灵活。
它可以在 “只定义具体组件的各种不同的单一装饰类” 的情况下,将这些单一装饰类进行动态地排列组合,产生大量不同的组合装饰效果(注意“排列”和“组合”都能产生不同)。而如果使用继承的方式,则需要将 单一装饰类 和 组合装饰类 都静态地定义出来。
装饰器模式,可以进行排列组合的核心在于,抽象装饰器可以持有另一个装饰器。
装饰器都是继承自抽象组件的。它实现的接口方法,都调用被装饰者的对应的接口方法。所以,这样的持有调用将让装饰方法一层层叠加起来。可以理解为是在装饰的基础上继续装饰。
用一个图来表示就很清晰了(横向可自由排列组合不同的装饰器),如下:
装饰器在只有一层装饰时(没有组合需求时),可以增加除了原接口方法之外的其他接口方法和属性(即,不用保持接口不变),但这不是装饰器模式想要表达的核心思想,不推荐这么干。
多层装饰时,最外层也可扩展额外的方法和属性。也不推荐这样干。
装饰器模式的结构和 代理模式 很相似,但其设计思想则完全不同。代理模式是为了对被代理者进行访问控制,不会对被代理者的职责进行扩展。
四、实现
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace Decorator
{
//抽象组件
public abstract class Component
{
public abstract void DoSth();
}
//具体组件
public class ConcretComponent : Component
{
public override void DoSth()
{
Debug.Log("做X事情");
}
}
//抽象装饰类
public abstract class Decorator : Component
{
public Component component;
public Decorator(Component component)
{
this.component = component;
}
//这里方便起见,将DoSth定位一个模板方法。实际可以由子类决定DoSth时,除了做原来的事,还要怎么做别的事。
public override void DoSth()
{
this.component.DoSth();
DoOtherThing();
}
public abstract void DoOtherThing();
}
//具体装饰类A
public class ConcreteDecoratorA : Decorator
{
public ConcreteDecoratorA(Component component) : base(component){ }
//增加其他事情
public override void DoOtherThing()
{
Debug.Log("做A事情");
}
}
//具体装饰类B
public class ConcreteDecoratorB : Decorator
{
public ConcreteDecoratorB(Component component) : base(component) { }
//增加其他事情
public override void DoOtherThing()
{
Debug.Log("做B事情");
}
}
//具体装饰类C
public class ConcreteDecoratorC : Decorator
{
public ConcreteDecoratorC(Component component) : base(component) { }
//增加其他事情
public override void DoOtherThing()
{
Debug.Log("做C事情");
}
}
public class Client
{
static public void Main()
{
//正常情况下,具体做X事。
ConcretComponent cc = new ConcretComponent();
cc.DoSth();
//现在想让组件类,除了能做X事,还能做A事。
//此时不用修改这个组件类。
//而是将其放入具体装饰器(或称为“包装器”更贴切)中。
ConcreteDecoratorA cdA = new ConcreteDecoratorA(cc);
ConcreteDecoratorB cdB = new ConcreteDecoratorB(cc);
ConcreteDecoratorB cdC = new ConcreteDecoratorB(cc);
cdA.DoSth(); //既能干X、又能干A
cdB.DoSth(); //既能干X、又能干B
cdC.DoSth(); //既能干X、又能干C
//------------------------------------------------------------------
//除了上边那样简单的单层装饰,还能进行任意组合的装饰
new ConcreteDecoratorB(cdA).DoSth(); //既能干X、又能干A、又能干B
new ConcreteDecoratorA(cdB).DoSth(); //同上,顺序变化
new ConcreteDecoratorC(cdA).DoSth(); //既能干X、又能干A、又能干C
new ConcreteDecoratorA(cdC).DoSth(); //同上,顺序变化
new ConcreteDecoratorC(cdB).DoSth(); //既能干X、又能干B、又能干C
new ConcreteDecoratorB(cdC).DoSth(); //同上,顺序变化
new ConcreteDecoratorA(new ConcreteDecoratorB(cdC)).DoSth(); //既能干X、又能干A、又能干B、又能干C
new ConcreteDecoratorA(new ConcreteDecoratorC(cdB)).DoSth(); //同上,顺序变化
new ConcreteDecoratorB(new ConcreteDecoratorA(cdC)).DoSth(); //同上,顺序变化
new ConcreteDecoratorB(new ConcreteDecoratorC(cdA)).DoSth(); //同上,顺序变化
new ConcreteDecoratorC(new ConcreteDecoratorA(cdB)).DoSth(); //同上,顺序变化
new ConcreteDecoratorA(new ConcreteDecoratorB(cdA)).DoSth(); //同上,顺序变化
//------------------------------------------------------------------
//甚至可以同方法多次装饰
new ConcreteDecoratorA(cdA).DoSth();
new ConcreteDecoratorA(new ConcreteDecoratorA(cdA)).DoSth();
}
}
}