定义:
装饰模式(Decorator Pattern) :动态地给一个对象增加一些额外的职责(Responsibility),就增加对象功能来说,装饰模式比生成子类实现更为灵活。其别名也可以称为包装器(Wrapper),与适配器模式的别名相同,但它们适用于不同的场合。根据翻译的不同,装饰模式也有人称之为“油漆工模式”,它是一种对象结构型模式。
装饰模式参与者
- Component:抽象的组件对象。
- ConcreteComponent:一个具体组件对象,也可以给这个对象添加一些职责。
- Decorator:装饰抽象类,继承了Component,从外类来扩展Component功能,但对Component来说,是无需知道Decorator的存在的。
- ConcreteDecorator:具体装饰类
装饰模式基本代码
Component类:
namespace DecoratorPattern.BasicStructure { /// <summary> /// 抽象组件类 /// </summary> abstract class Component { public abstract void Operation(); } }
ConcreteComponent类:
namespace DecoratorPattern.BasicStructure { /// <summary> /// 具体组件类 /// </summary> class ConcreteComponent : Component { public override void Operation() { Console.WriteLine("具体对象的操作"); } } }
Decorator类:
namespace DecoratorPattern.BasicStructure { /// <summary> /// 装饰抽象类 /// </summary> abstract class Decorator : Component { protected Component Component { get; set; } public void SetComponent(Component component) { this.Component = component; } public override void Operation() { if (Component != null) { Component.Operation(); } } } }
ConcreteDecoratorA类:
namespace DecoratorPattern.BasicStructure { /// <summary> /// 具体装饰类 A /// </summary> class ConcreteDecoratorA : Decorator { private string _addedState; public override void Operation() { base.Operation(); _addedState = "new state"; Console.WriteLine("具体装饰对象A的操作"); } } }
ConcreteDecoratorB类:
namespace DecoratorPattern.BasicStructure { /// <summary> /// 具体装饰类 B /// </summary> class ConcreteDecoratorB : Decorator { public override void Operation() { base.Operation(); AddedBehavior(); Console.WriteLine("具体装饰对象B的操作"); } private void AddedBehavior() { } } }
客户端调用代码:
static void Main(string[] args) { try { {//BasicStructure ConcreteComponent c = new ConcreteComponent(); ConcreteDecoratorA d1 = new ConcreteDecoratorA(); ConcreteDecoratorB d2 = new ConcreteDecoratorB(); d1.SetComponent(c); d2.SetComponent(d1); d2.Operation(); } } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadKey(); }
结果如下:
用装饰模式实现给苹果手机贴膜和带手机套
场景模拟:某人最近新买了一部苹果XS,于是准备给手机贴膜和买个手机套带。
Handset(手机抽象)类——Component类
namespace DecoratorPattern.SituationSimulation { /// <summary> /// 手机抽象类,即装饰者模式中的抽象组件类 /// </summary> abstract class Handset { public abstract void Operation(); } }
AppleHandset(苹果手机)类——ConcreteComponent类
namespace DecoratorPattern.SituationSimulation { /// <summary> /// 苹果手机手机类,即装饰着模式中的具体组件类 /// </summary> class AppleHandset : Handset { public override void Operation() { Console.WriteLine("操作苹果手机"); } } }
HandsetDecorator(手机装饰)类——Decorator类
namespace DecoratorPattern.SituationSimulation { abstract class HandsetDecorator : Handset { protected Handset Handset { get; set; } public HandsetDecorator(Handset handset) { this.Handset = handset; } public override void Operation() { if (Handset != null) { Handset.Operation(); } } } }
HandsetSticker(手机贴膜)类——ConcreteDecorator类
namespace DecoratorPattern.SituationSimulation { /// <summary> /// 贴纸 /// </summary> class HandsetSticker : HandsetDecorator { public HandsetSticker(Handset handset) : base(handset) { } public override void Operation() { base.Operation(); AddSticker(); } public void AddSticker() { Console.WriteLine("给手机贴膜"); } } }
HandsetCasing(手机保护套)类——ConcreteDecorator类
namespace DecoratorPattern.SituationSimulation { /// <summary> /// 手机保护套 /// </summary> class HandsetCasing : HandsetDecorator { public HandsetCasing(Handset handset) : base(handset) { } public override void Operation() { base.Operation(); AddCasing(); } public void AddCasing() { Console.WriteLine("给手机戴保护套"); } } }
客户端调用代码:
static void Main(string[] args) { try { {//SituationSimulation //第一种写法 Console.WriteLine("***************第一种写法:***************"); AppleHandset handset = new AppleHandset(); HandsetSticker handsetSticker = new HandsetSticker(handset); HandsetCasing handsetCasing = new HandsetCasing(handsetSticker); handsetCasing.Operation(); //第二种写法 Console.WriteLine("***************第二种写法:***************"); Handset appleHandset = new AppleHandset(); appleHandset = new HandsetSticker(appleHandset); appleHandset = new HandsetCasing(appleHandset); appleHandset.Operation(); //第二种写法的简化 Console.WriteLine("***************第二种写法的简化:***************"); new HandsetCasing(new HandsetSticker(new AppleHandset())).Operation(); } } catch (Exception ex) { Console.WriteLine(ex.Message); } Console.ReadKey(); }
结果如下:
优点:
- 装饰模式与继承关系的目的都是要扩展对象的功能,但是装饰模式可以提供比继承更多的灵活性。
- 可以通过一种动态的方式来扩展一个对象的功能,通过配置文件可以在运行时选择不同的装饰器,从而实现不同的行为。
- 通过使用不同的具体装饰类以及这些装饰类的排列组合,可以创造出很多不同行为的组合。可以使用多个具体装饰类来装饰同一对象,得到功能更为强大的对象。
- 具体构件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开闭原则”
缺点:
- 使用装饰模式进行系统设计时将产生很多小对象,这些对象的区别在于它们之间相互连接的方式有所不同,而不是它们的类或者属性值有所不同,同时还将产生很多具体装饰类。这些装饰类和小对象的产生将增加系统的复杂度,加大学习与理解的难度。
- 这种比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。
适用环境:
在以下情况下可以使用装饰模式:
- 在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
- 需要动态地给一个对象增加功能,这些功能也可以动态地被撤销。
- 当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。不能采用继承的情况主要有两类:第一类是系统中存在大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸性增长;第二类是因为类定义不能继承(如final类).
总结:
- 装饰模式用于动态地给一个对象增加一些额外的职责,就增加对象功 能来说,装饰模式比生成子类实现更为灵活。它是一种对象结构型模 式。
- 装饰模式包含四个角色:抽象构件定义了对象的接口,可以给这些对 象动态增加职责(方法);具体构件定义了具体的构件对象,实现了 在抽象构件中声明的方法,装饰器可以给它增加额外的职责(方法); 抽象装饰类是抽象构件类的子类,用于给具体构件增加职责,但是具 体职责在其子类中实现;具体装饰类是抽象装饰类的子类,负责向构 件添加新的职责。
- 使用装饰模式来实现扩展比继承更加灵活,它以对客户透明的方式动 态地给一个对象附加更多的责任。装饰模式可以在不需要创造更多子 类的情况下,将对象的功能加以扩展。
- 装饰模式的主要优点在于可以提供比继承更多的灵活性,可以通过一种动态的 方式来扩展一个对象的功能,并通过使用不同的具体装饰类以及这些装饰类的 排列组合,可以创造出很多不同行为的组合,而且具体构件类与具体装饰类可 以独立变化,用户可以根据需要增加新的具体构件类和具体装饰类;其主要缺 点在于使用装饰模式进行系统设计时将产生很多小对象,而且装饰模式比继承 更加易于出错,排错也很困难,对于多次装饰的对象,调试时寻找错误可能需 要逐级排查,较为烦琐。
- 装饰模式适用情况包括:在不影响其他对象的情况下,以动态、透明的方式给 单个对象添加职责;需要动态地给一个对象增加功能,这些功能也可以动态地 被撤销;当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展 和维护时。
- 装饰模式可分为透明装饰模式和半透明装饰模式:在透明装饰模式中,要求客 户端完全针对抽象编程,装饰模式的透明性要求客户端程序不应该声明具体构 件类型和具体装饰类型,而应该全部声明为抽象构件类型;半透明装饰模式允 许用户在客户端声明具体装饰者类型的对象,调用在具体装饰者中新增的方法。