装饰者模式的应用场景
装饰者模式(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 {
@Override
protected String getMsg() {
return super.getMsg() + "+1个鸡蛋";
}
/**
* 加1个鸡蛋加1元钱
*
* @return
*/
@Override
public int getPrice() {
return super.getPrice() + 1;
}
}
再创建一个既加鸡蛋有加香肠的 BattercakeWithEggAndSausage 类:
package com.yjw.demo.pattern.decorator2.v1;
public class BattercakeWithEggAndSausage extends BattercakeWithEgg {
@Override
protected String getMsg() {
return super.getMsg() + "+1跟香肠";
}
/**
* 加1根香肠加2元钱
*
* @return
*/
@Override
public 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 {
@Override
protected String getMsg() {
return "煎饼";
}
@Override
protected 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();
@Override
protected String getMsg() {
return this.battercake.getMsg();
}
@Override
protected 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);
}
@Override
protected void doSomething() {
}
@Override
protected String getMsg() {
return super.getMsg() + "+1个鸡蛋";
}
@Override
protected 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);
}
@Override
protected void doSomething() {
}
@Override
protected String getMsg() {
return super.getMsg() + "+1根香肠";
}
@Override
protected 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 来源:殷建卫 - 架构笔记 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。