装饰器模式(Decorator Pattern)就是在原有的对象上边再添加一些方法或者值;用菜鸟教程的说法就是:允许向一个现有的对象添加新的功能,同时又不改变其结构。
是对原来的结构和功能整体保留,而改变的是该方式内部的值。
简单的理解就是,将一个对象再作进一步的修饰,让其更加的丰富。但是,又不改变本身的结构。
一、优缺点
优点:装饰类和被装饰类可以独立发展,不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。
缺点:多层装饰比较复杂。
二、设计原则
装饰器模式将现有对象和装饰器进行分离,两者独立存在,符合开放封闭原则。
三、代码实现
我们已经知道了装饰器只是一个对原有对象进行修饰的一种的方式。
那么我们开始构造一个装饰器。
我们每天早餐要想吃得健康,那么就必须要丰富一些。我们先构建一个方法,用来买主食,喝粥吧
function Porridge (price,nums) {
console.log('今天喝粥啦!');
this.price = price * nums;
}
我们只是和粥可能不够营养,那么我们索性加鸡蛋,如下
function addEggs(mainBreakfast, price, nums) {
console.log('加鸡蛋了喂!');
mainBreakfast.price += price * nums;
}
可能价格鸡蛋也不太饱啊,还没有什么喝的,那么来一杯牛奶啦!
function addMilk(mainBreakfast, price, nums) {
console.log('加牛奶了喂!');
mainBreakfast.price += price * nums;
}
这个时候牛,我们的装饰器就构建出来了。我们运行一下看看效果:
var porridge = new Porridge(2, 1);
addEggs(porridge, 3, 2); // 我就是要吃两个
addMilk(porridge, 5, 1)
console.log(porridge.price)
okay,装饰器成功了。而且,喝粥这个结构也是完好如初的。这个跟适配器模式有很大的不一样。适配器模式是搞了一个接口,然后将旧的对象改造成现在需要用的对象,是抛弃旧对象;而装饰器是为了丰富旧对象,而不是将它彻底改造。
我们用es6的class来改造一下这个早餐的装饰器模式吧。
class Porridge {
constructor(price, nums) {
this.price = price * nums
this.nums = nums
}
priceAddFn() {
console.log(this.price)
}
}
class addEggs {
constructor(shape) {
this.shape = shape
}
priceAddFn(price, nums) {
console.log('加鸡蛋了喂!')
this.shape.price += price * nums
console.log(this.shape.price)
}
}
class addMilk {
constructor(shape) {
this.shape = shape
}
priceAddFn(price, nums) {
console.log('加牛奶了喂!')
this.shape.price += price * nums
console.log(this.shape.price)
}
}
let porridge = new Porridge(3, 1)
let addeggs = new addEggs (porridge)
let addmilk = new addMilk (porridge)
addeggs.priceAddFn(2, 2)
addmilk.priceAddFn(4, 1)
总结: 装饰器是比较简单的设计模式,只需要记住一点就是,在原来的结构和功能上进行独立解耦的添加方法就行。
参考文章:
1、JavaScript装饰器模式 https://www.jianshu.com/p/bd389638aa4b
2、装饰器模式 https://www.runoob.com/design-pattern/decorator-pattern.html