装饰模式 问题引入 问题描述 想要给人一个较好的印象,穿衣搭配得体干净是不可或缺的。我们可以使用程序写一个可以给人搭配不同服饰的系统,比如类似QQ、电商平台或游戏都有的Avatar系统,实现给人搭配嘻哈服或白领装的功能。程序预期的输出效果如下图所示。 为实现这一程序,可先定义Person类,将服饰装扮都写为Person类的接口方法。但如若要在此基础上增加“超人”的装扮,就需要修改Person类,会违背开放-封闭原则,因此应考虑将服饰单独为类。 同时,由于穿衣过程不能在众目睽睽之下完成,应当考虑在类内部组装完毕,再显示。而类内部的服饰组装过程不是固定的,因为通过服饰组合出一个有个性的人完全可以有无数种方案,如上衣大T恤,既可以搭配球鞋,也可以搭配皮鞋。
想要给人一个较好的印象,穿衣搭配得体干净是不可或缺的。我们可以使用程序写一个可以给人搭配不同服饰的系统,比如类似QQ、电商平台或游戏都有的Avatar系统,实现给人搭配嘻哈服或白领装的功能。程序预期的输出效果如下图所示。

为实现这一程序,可先定义Person类,将服饰装扮都写为Person类的接口方法。但如若要在此基础上增加“超人”的装扮,就需要修改Person类,会违背开放-封闭原则,因此应考虑将服饰单独为类。

同时,由于穿衣过程不能在众目睽睽之下完成,应当考虑在类内部组装完毕,再显示。而类内部的服饰组装过程不是固定的,因为通过服饰组合出一个有个性的人完全可以有无数种方案,如上衣大T恤,既可以搭配球鞋,也可以搭配皮鞋。同时服饰的装扮的先后顺序也有一定讲究,毕竟先穿内裤后穿外裤和先穿外裤再穿内裤是截然不同的效果。也就是说,我们需要将所需功能按正确的顺序串联起来进行控制,此时可以考虑使用装饰模式。
装饰模式(Decorator Pattern)是指创建一个装饰类,来包装原有的类,从而实现动态地向一个现有的对象添加一些额外的职责,同时不改变其原有的结构。装饰模式比生成子类更为灵活。
装饰模式用在此问题较为直观,衣服、鞋子、领带、披风等服饰都可以理解为对人的装饰。在实现Person类表示人的基础上,定义具体的服饰类实现对Person类进行服装装饰,装扮完再统一显示,这样既避免了穿每一件衣服的过程直接在客户端实现,同时又保留服装搭配的灵活性。
使用装饰模式来解决问题。
Component,定义给对象动态添加职责的公共接口(在此例中,由于具体的接口只有一个,所以该步也可省略);Person(Concrete Component),继承于抽象接口类Component,同时:
Show()用于显示装扮结果;Finery(Decorator),继承于接口类Person(一般来说继承于抽象接口类Component,由于此例只有一个接口,故继承于具体接口类),同时:
Decorate(component)用于进行装扮过程;Show()具体装扮结果的显示;Tshirts,BigTrouser等,继承于抽象装饰类Finery,实现具体的装饰对象,同时:
Show()具体装扮结果的显示。此处我们使用Java语言来实现这一方案,C#语言实现可见原书原版,本项目的所有语言实现可见本项目Github仓库,其中包括:C++,Java,python,读者可按需参阅。
定义Person类,相当于ConcreteComponent。
public class Person { public Person() {} private String name; public Person(String name) { this.name = name; } public void show() { System.out.println("装扮的" + this.name); } }
定义Finery类,相当于Decorator。
public class Finery extends Person { protected Person component; public void decorate(Person component) { this.component = component; } @Override public void show() { if(component != null) { component.show(); } } }
具体的服饰类,相当于ConcreteDecorator,这里以Tshirts、Sneakers、BigTrouser为例,其余类类似,代码省略。
public class TShirts extends Finery{ @Override public void show() { System.out.println("大T恤"); super.show(); } } public class Sneakers extends Finery{ @Override public void show() { System.out.println("运动鞋"); super.show(); } } public class BigTrouser extends Finery{ @Override public void show() { System.out.println("跨裤"); super.show(); } }
客户端如下。
public class DecoratorClient { public static void main(String[] args) { Person person = new Person("小菜"); System.out.println("第一种装扮:"); Sneakers sneaker = new Sneakers(); BigTrouser trouser = new BigTrouser(); TShirts tshirt = new TShirts(); sneaker.decorate(person); trouser.decorate(sneaker); tshirt.decorate(trouser); tshirt.show(); } }
运行结果如下。
第一种装扮: 大T恤 垮裤 破球鞋 装扮的小菜
装饰模式由四类主要角色组成:
结构示意图如下

装饰模式的通用结构示意图如下

可以在不生成很多子类的情况下扩展类,适用于扩展类需求较多,而又不想引起子类膨胀的场景。
装饰模式的优点有
装饰模式的缺点有