简介
装饰器模式(Decorator Pattern)也叫作包装器模式(Wrapper Pattern),指在不改变原有对象的基础上,动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更为灵活,属于结构型设计模式。
通用模板
-
创建抽象组件接口:可以是一个接口或者抽象类,充当被装饰类的原始对象,规定了被装饰对象的行为。
// 被装饰的组件接口 public interface IComponent {// 被装饰对象的行为void describe(); }
-
创建具体组件:实现/继承Component的一个具体对象,即被装饰对象。
// 具体的被装饰类 public class Component implements IComponent{@Overridepublic void describe() {System.out.println("我是未被装饰的基础组件");} }
-
创建抽象装饰器基类:通用的装饰ConcreteComponent的装饰器,其内部必然有一个属性指向Component;其实现一般是一个抽象类,主要为了让其子类按照其构造形式传入一个Component,这是强制的通用行为。如果系统中装饰逻辑单一,则并不需要实现许多装饰器,可以直接省略该类,而直接实现一个具体装饰器即可。
// 装饰器基类 public abstract class BaseDecorator implements IComponent {private IComponent iComponent;public BaseDecorator(IComponent iComponent) {this.iComponent = iComponent;}@Overridepublic void describe() {iComponent.describe();} }
-
创建具体装饰器类:理论上,每个ConcreteDecorator都扩展了Component对象的一种功能。
// 具体的装饰器类A public class DecoratorA extends BaseDecorator {public DecoratorA(IComponent iComponent) {super(iComponent);}@Overridepublic void describe() {super.describe();System.out.println("我被装饰器A装饰了");} }
// 具体的装饰器类B public class DecoratorB extends BaseDecorator {public DecoratorB(IComponent iComponent) {super(iComponent);}@Overridepublic void describe() {super.describe();System.out.println("我被装饰器B装饰了");} }
模板测试
-
代码
public class Client {public static void main(String[] args) {Component component = new Component();// 用装饰器A来装饰组件DecoratorA decoratorA = new DecoratorA(component);// 用装饰器B来装饰已经被装饰器A装饰过后的组件DecoratorB decoratorB = new DecoratorB(decoratorA);decoratorB.describe();} }
-
结果
我是未被装饰的基础组件 我被装饰器A装饰了 我被装饰器B装饰了
应用场景
来看这样一个场景,上班族大多有睡懒觉的习惯,每天早上上班时间都很紧张,于是很多人为了多睡一会儿,就用更方便的方式解决早餐问题,有些人早餐可能会吃煎饼。煎饼中可以加鸡蛋,也可以加香肠,但是不管怎么“加码”,都还是一个煎饼。再比如,给蛋糕加上一些水果,给房子装修,都是装饰器模式。 装饰器模式在代码程序中适用于以下应用场景。
(1)用于扩展一个类的功能,或者给一个类添加附加职责。
(2)动态地给一个对象添加功能,这些功能可以再动态地被撤销。
(3)需要为一批平行的兄弟类进行改装或加装功能。
优点
(1)装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态地给一个对象扩展功能,即插即用。
(2)通过使用不同装饰类及这些装饰类的排列组合,可以实现不同效果。
(3)装饰器模式完全遵守开闭原则。
缺点
(1)会出现更多的代码、更多的类,增加程序的复杂性。
(2)动态装饰在多层装饰时会更复杂。
“生搬硬套”实战
场景描述
让我们通过一个给房子装修的场景来演示装饰器模式。假设我们有一栋房子,需要逐步对其进行装修,包括刷墙漆、安装窗帘、挂画等。
-
创建抽象组件(这里指房子)接口
public interface House {void describe(); }
-
创建具体组件(这里指空房子)
public class EmptyHouse implements House {@Overridepublic void describe() {System.out.println("This is an empty house.");} }
-
创建抽象装饰器(这里指装修)基类
public abstract class HouseDecorator implements House {protected House house;public HouseDecorator(House house) {this.house = house;}@Overridepublic void describe() {house.describe();} }
-
创建具体装饰器(这里指刷墙漆、安装窗帘、挂艺术作品)类
public class PaintDecorator extends HouseDecorator {private String color;public PaintDecorator(House house, String color) {super(house);this.color = color;}@Overridepublic void describe() {super.describe();System.out.println("The walls have been painted " + color + ".");} }
public class CurtainDecorator extends HouseDecorator {private String material;public CurtainDecorator(House house, String material) {super(house);this.material = material;}@Overridepublic void describe() {super.describe();System.out.println("There are " + material + " curtains installed.");} }
public class ArtworkDecorator extends HouseDecorator {private int numberOfPieces;public ArtworkDecorator(House house, int numberOfPieces) {super(house);this.numberOfPieces = numberOfPieces;}@Overridepublic void describe() {super.describe();System.out.println("There are " + numberOfPieces + " pieces of art hanging on the walls.");} }
至此,我们就通过“生搬硬套”装饰器模式的模板设计出一套装修房子的代码,接下来我们进行测试:
-
测试代码
public class Main {public static void main(String[] args) {// 创建一个空房子House emptyHouse = new EmptyHouse();// 装修:刷墙漆House paintedHouse = new PaintDecorator(emptyHouse, "blue");// 装修:安装窗帘House curtainedHouse = new CurtainDecorator(paintedHouse, "silk");// 装修:挂艺术作品House decoratedHouse = new ArtworkDecorator(curtainedHouse, 5);// 描述最终的装修结果decoratedHouse.describe();} }
-
结果
This is an empty house. The walls have been painted blue. There are silk curtains installed. There are 5 pieces of art hanging on the walls.
装饰器模式与代理模式的区别
这两种模式都是扩展某个类的功能,但是这两种设计模式所面向的功能扩展面是不一样的。
- 装饰器模式强调自身功能的扩展。
- 代理模式强调对代理过程的控制。
简单来讲,假设现在小明想租房,那么势必会有一些事务发生:房源搜索、联系房东谈价格等。 假设按照代理模式进行思考,那么小明只需找到一个房产中介,让他去做房源搜索、联系房东谈价格这些事情,小明只需等待通知然后付中介费就行了。 而如果采用装饰器模式进行思考,因为装饰器模式强调的是自身功能扩展,也就是说,如果要找房子,小明自身就要增加房源搜索能力扩展、联系房东谈价格能力扩展,通过相应的装饰器,提升自身能力,一个人做完所有的事情。
那么我们从写代码的层面来看:
装饰者模式是使用的调用者从外部传进来的被装饰对象,调用者只想让你把他给你的对象加强一下,装饰一下. 代理模式使用的是代理对象在自己的构造方法里面new的一个被代理类的对象,不是调用者传入的,调用者不知道你找了其他人,他也不关心这些事,只要你把事情做好就行了.
那么也就是说使用代理的时候是不需要传入被代理的对象。
总结
装饰器模式提供了比继承更有弹性的替代方案(扩展原有对象的功能)将功能附加到对象上。因此,装饰器模式的核心是功能扩展。使用装饰器模式可以透明且动态地扩展类的功能。