定义
装饰模式,顾名思义就是允许向一个现有的对象增加新功能,满足复杂需求,同时又满足开闭原则,不破坏现有结构。这种类型的设计模式是结构型模式,作为现有类的一个包装。
好吃的手抓饼
关于装饰模式在生活中的例子也有很多,比如:老板,来个手抓饼,加个鸡蛋,加跟香肠,多少钱? 是不是觉得这句话耳熟能详呢,甚至自己也经常说。我们来看这里手抓饼是核心基础角色,鸡蛋、香肠是配菜,是用来装饰角色,然后可能根据顾客的口味不同还会添加其他很多的配菜。那么在代码中如何表示加了鸡蛋香肠的手抓饼或者加了辣条的手抓饼呢?
首先想到的是子类继承方式,但是这样的话,会出现很多子类,加蛋手抓饼、加蛋加肠手抓饼、加辣条手抓饼、加薄脆手抓饼……,很显然我们手抓饼的子类多少是由配菜的数量来决定的,如果后续配菜增多,我们的子类数量也是比较庞大的。
这个时候就可以选择使用装饰模式来实现,手抓饼是主体,然后运行时以配菜来装饰手抓饼。比如说顾客想要一个加蛋加肠的手抓饼。那么可以先来一个基础的手抓饼,然后再来一个鸡蛋对象来装饰它,再来一个香肠对象来装饰它,这样后续有其他的不一样的需求时,只需要使用对应的配菜进行装饰即可。代码如下
// 手抓饼的抽象类
class HandCake {
offerHandCake() {
}
calcConst() {
}
}
// 基础手抓饼
class BaseHandCake extends HandCake{
constructor() {
super()
}
offerHandCake() {
return '基础手抓饼'
}
calcConst() {
return 2
}
}
// 配菜抽象类(装饰器)
class Decorator extends HandCake{
constructor(baseHandcake) {
super(baseHandcake)
this.baseHandcake = baseHandcake
}
offerHandCake() {
return this.baseHandcake.offerHandCake()
}
calcConst() {
return this.baseHandcake.calcConst()
}
}
// 加蛋手抓饼
class Egg extends Decorator{
constructor(baseHandcake) {
super(baseHandcake)
}
offerHandCake() {
return super.offerHandCake()+'加鸡蛋'
}
calcConst() {
return super.calcConst()+2
}
}
const myHandCake = new Egg(new BaseHandCake()).offerHandCake()
const totalConst = new Egg(new BaseHandCake()).calcConst()
console.log(`购买${myHandCake},共花费${totalConst}元`) // 购买基础手抓饼加鸡蛋,共花费4元
装饰模式优点
- 装饰类和被装饰类可以独立发展,不会相互耦合;
- 装饰模式是继承的一个替代模式;
- 装饰模式可以动态扩展一个实现类的功能。
装饰模式缺点
- 多层装饰比较复杂,不便定位问题,比如查找问题时,被层层嵌套,不容易发现问题所在。
- 装饰器模式虽然从数量级上减少了类的数量,但是为了要装饰,仍旧会增加很多具体的实现类,这些具体的装饰类的逻辑将不会非常的清晰,不够直观,容易令人迷惑
使用场景
- 当你想要给一个类增加功能,却并不想修改原来类的代码时,可以考虑装饰器模式;
- 动态的给一个类增加功能,并且这个功能你还希望可以动态的撤销;
- 需要为一批的兄弟类进行改装或加装功能时。