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