装饰者模式的应用场景
装饰者模式(Decorator Pattern)是指在不改变原有对象的基础上,将功能附加到对象上,提供了比继承更有弹性的方案(扩展原有对象的功能),属于结构型模式。装饰器模式适用于以下场景:
- 扩展一个类的功能或给一个类添加附加职责。
- 动态给一个对象添加功能,这些功能可以再动态地撤销。
来看一个这样的场景。上班族大多有睡懒觉的习惯,每天早上上班都“踩着点”,于是很多“小伙伴”为了多懒一会儿床都不吃早餐。也有些“小伙伴”可能在上班路上碰到卖煎饼的路边摊,会带一个到公司茶水间吃。卖煎饼的大姐可以给你的煎饼加鸡蛋,也可以加香肠。
下面我们用代码还原一下这个生活场景。首先创建一个煎饼类 Battercake:
package com.yjw.demo.pattern.decorator2.v1;public class Battercake {protected String getMsg() {return "煎饼";}public int getPrice() {return 5;}}
然后创建一个加鸡蛋的煎饼类 BattercakeWithEgg:
package com.yjw.demo.pattern.decorator2.v1;public class BattercakeWithEgg extends Battercake {@Overrideprotected String getMsg() {return super.getMsg() + "+1个鸡蛋";}/*** 加1个鸡蛋加1元钱** @return*/@Overridepublic int getPrice() {return super.getPrice() + 1;}}
再创建一个既加鸡蛋有加香肠的 BattercakeWithEggAndSausage 类:
package com.yjw.demo.pattern.decorator2.v1;public class BattercakeWithEggAndSausage extends BattercakeWithEgg {@Overrideprotected String getMsg() {return super.getMsg() + "+1跟香肠";}/*** 加1根香肠加2元钱** @return*/@Overridepublic int getPrice() {return super.getPrice() + 2;}}
编写客户端测试代码:
package com.yjw.demo.pattern.decorator2.v1;public class BattercakeTest {public static void main(String[] args) {Battercake battercake = new Battercake();System.out.println(battercake.getMsg() + ",总价格:" + battercake.getPrice());Battercake battercakeWithEgg = new BattercakeWithEgg();System.out.println(battercakeWithEgg.getMsg() + ",总价格:" + battercakeWithEgg.getPrice());Battercake battercakeWithEggAndSausage = new BattercakeWithEggAndSausage();System.out.println(battercakeWithEggAndSausage.getMsg() + ",总价格:" + battercakeWithEggAndSausage.getPrice());}}
运行结果如下图所示。

运行结果没有问题。但是,如果用户需要一个加 2 个鸡蛋、加 1 根香肠的煎饼,用我们现在的类结构是创建不出来的,也无法自动计算出价格,除非再创建一个类做定制。如果需求再变,一直做定制显然是不科学的。下面我们就用装饰器模式来解决上面的问题。首先,创建一个煎饼的抽象类 Battercake:
package com.yjw.demo.pattern.decorator2.v2;public abstract class Battercake {protected abstract String getMsg();protected abstract int getPrice();}
然后,创建一个基本煎饼类 BaseBattercake:
package com.yjw.demo.pattern.decorator2.v2;public class BaseBattercake extends Battercake {@Overrideprotected String getMsg() {return "煎饼";}@Overrideprotected int getPrice() {return 5;}}
再创建一个扩展套餐的抽象装饰器类 BattercakeDecorator:
package com.yjw.demo.pattern.decorator2.v2;public abstract class BattercakeDecorator extends Battercake {// 静态代理,委派private Battercake battercake;public BattercakeDecorator(Battercake battercake) {this.battercake = battercake;}protected abstract void doSomething();@Overrideprotected String getMsg() {return this.battercake.getMsg();}@Overrideprotected int getPrice() {return this.battercake.getPrice();}}
接下来,创建鸡蛋装饰器类 EggDecorator:
package com.yjw.demo.pattern.decorator2.v2;public class EggDecorator extends BattercakeDecorator {public EggDecorator(Battercake battercake) {super(battercake);}@Overrideprotected void doSomething() {}@Overrideprotected String getMsg() {return super.getMsg() + "+1个鸡蛋";}@Overrideprotected int getPrice() {return super.getPrice() + 1;}}
最后,创建香肠装饰器类 SausageDecorator:
package com.yjw.demo.pattern.decorator2.v2;public class SausageDecorator extends BattercakeDecorator {public SausageDecorator(Battercake battercake) {super(battercake);}@Overrideprotected void doSomething() {}@Overrideprotected String getMsg() {return super.getMsg() + "+1根香肠";}@Overrideprotected int getPrice() {return super.getPrice() + 2;}}
编写客户端测试代码:
package com.yjw.demo.pattern.decorator2.v2;public class BattercakeTest {public static void main(String[] args) {Battercake battercake;// 路边摊买一个煎饼battercake = new BaseBattercake();// 加1个鸡蛋battercake = new EggDecorator(battercake);// 再加1个鸡蛋battercake = new EggDecorator(battercake);// 再加1根香肠battercake = new SausageDecorator(battercake);System.out.println(battercake.getMsg() + ",总价格:" + battercake.getPrice());}}
运行结果如下图所示。

装饰者模式和适配器模式对比
装饰器模式和适配器模式都是包装模式(Wrapper Pattern),装饰者模式也是一种特殊的代理模式,二者对比如下表所示。
| 装饰者模式 | 适配器模式 | |
|---|---|---|
| 形式 | 是一种非常特别的适配器模式 | 没有层级关系,装饰者模式有层级关系 |
| 定义 | 装饰者和被装饰者实现同一个接口,主要目的是扩展之后依旧保留OOP关系 | 适配器和被适配者没有必然的联系,通常采用继承或代理的形式进行包装 |
| 关系 | 满是is-a的关系 | 满足has-a的关系 |
| 功能 | 注重覆盖、扩展 | 注重兼容、转换 |
| 设计 | 前置考虑 | 后置考虑 |
装饰者模式的优缺点
装饰者模式的优点如下:
- 装饰者模式是继承的有力补充,且比继承灵活,可以在不改变原有对象的情况下动态地给一个对象扩展功能,即插即用。
- 使用不同的装饰器类及这些装饰类的排列组合,可以实现不同的效果。
- 装饰器模式完全符合开闭原则。
装饰器模式的缺点如下:
- 会出现更多的代码、更多的类,增加程序的复杂性。
- 动态装饰时,多层装饰会更复杂。
摘录:《Spring 5 核心原理与30个类手写实战》来自文艺界的Tom老师的书籍。
作者:殷建卫 链接:https://www.yuque.com/yinjianwei/vyrvkf/gn3qgn 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
