概述:
装饰模式又名包装(Wrapper)模式,是以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
意图:
动态方式给一个对象附加更多的功能。装饰模式可以在不创造更多的子类的情况下扩张对象的功能。动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更加灵活(为什么比继承要好,后面会讲到)。
实现对客户端透明 (怎么实现,后面会讲到)
实质:
实现对客户端透明的方式扩展对象的功能。
使用场景:
主要是解决:“过度地使用了继承来扩展对象的功能”,由于继承为类型引入的静态特质(以继承的方式使某一类型要获得功能是在编译时是静态的,动态,是指在运行时),使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀(多继承)。另一种情况可能是因为类定义被隐藏,或类定义不能用于生成子类。
我的理解:我们要扩展对象的功能(即对已有功能动态添加更多功能)而且还要实现对客户端透明这个是本质。
如果我们可以继承ConcreteComponent,生成A, B, C;如果客户端需要功能不确定,动态的,有可能是A、AC、CAB等等。由于实现对客户端透明,所以得继续扩充产生AC、CAB等等。这样的话虽然功能可以实现,但以后继续扩充会导致子类的膨胀,而且代码的重用性很差,所以不能通过继承来扩充功能。如果ConcreteComponent被隐藏(我们访问不到)或者不能生成子类。这样的话我们根本就不能通过继承来扩充功能。
透明性: 要求客户端的程序不要声明一个ConcreteComponent类型的变量,而应当声明一个Component接口类型的变量。
我们要扩展对象的功能(对已有功能动态扩充添加)但又不能继承(这里是指不能继承ConcreteComponent来扩展功能)。意味着需要把它的功能进行装饰,所以只能是has-a的关系,由于我们需要Operation方法名调用ConcreteComponent的Operation,所以要继承Component接口,以实现对客户端的透明。
参与者:
- 抽象构件角色(Component):定义一个对象接口,可以给这些对象动态地添加职责。
- 具体构件角色(Concrete Component):定义了一个具体的对象,也可以给这个对象添加一些职责。
- 装饰角色(Decorator):持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的抽象类。
- 具体装饰角色(Concrete Decorator):具体的装饰对象,起到给Component添加职责的功能
UML图:
装饰模式的简化
如果只有一个ConcreteComponent类,那么可以考虑去掉抽象的Component类(接口),把Decorator作为一个ConcreteComponent子类。如下图所示:
如果只有一个ConcreteDecorator类,那么就没有必要建立一个单独的Decorator类,而可以把Decorator和ConcreteDecorator的责任合并成一个类。甚至在只有两个ConcreteDecorator类的情况下,都可以这样做。如下图所示:
基本条件:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public interface Component { void Operation(); } public class ConcreteComponent : Component { public void Operation() { //已有的功能 } }
装饰模式:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
public abstract class Decorator : Component { private Component component; public void SetComponent(Component component) { this.component = component; } public virtual void Operation() { if (component != null) component.Operation(); } } public class ConcreteDecoratorA : Decorator { private string AddedState; public override void Operation() { base.Operation(); AddedState = "装饰模式"; } } public class ConcreteDecoratorB : Decorator { public override void Operation() { base.Operation(); AddedBehavior(); } private void AddedBehavior() { //添加功能 } }
优点:
- 装饰模式提供了更加灵活的向对象添加职责的方式。在运行时装饰需要的职责,组合出复杂的功能。
- 把类中装饰功能从类中搬移去除,简化了原有类,有效地把核心职责和装饰功能区分开了,增加了代码的重用性。
缺点:
- 客户端需要自己去组合要装饰的功能。
装饰模式与适配器模式:
半透明装饰模式:客户端可以声明ConcreteDecorator类型的变量,从而可以调用ConcreteDecorator类中才有的方法。
装饰模式有透明和半透明两种,这两种的区别就在于装饰角色的接口与抽象构件角色的接口是否完全一致。透明的装饰模式也就是理想的装饰模式,要求具体构件角色、装饰角色的接口与抽象构件角色的接口完全一致。相反,如果装饰角色的接口与抽象构件角色接口不一致,也就是说装饰角色的接口比抽象构件角色的接口宽的话,装饰角色实际上已经成了一个适配器角色,这种装饰模式也是可以接受的,称为“半透明”的装饰模式,如下图所示。
在适配器模式里面,适配器类的接口通常会与目标类的接口重叠,但往往并不完全相同。换言之,适配器类的接口会比被装饰的目标类接口宽。
显然,半透明的装饰模式实际上就是处于适配器模式与装饰模式之间的灰色地带。如果将装饰模式与适配器模式合并成为一个“包装模式”的话,那么半透明的装饰模式倒可以成为这种合并后的“包装模式”的代表。
总结:以上纯属个人的理解,对于有些地方觉得还是理解不是很深,有不足之处和错误的地方希望大家帮我指出。谢谢