装饰者模式
什么是装饰者模式
所谓装饰者模式就是在不改变对象自身的基础上,允许向对象添加新功能的一种实现形式。
优点
- 装饰者模式比继承灵活性,在不改变原有对象的情况下给对象扩展功能,符合开闭原则。
- 装饰者模式可以动态使用不同的装饰类排列组合,创造出多样的行为组合。
缺点:
- 增加系统的复杂性。
- 对于多次装饰的对象,一旦出现错误,排错繁琐
功能实现
生活中有很多场景可以用装饰者模式来解释。比如你去星巴克买一杯咖啡,你可以选择要不要加糖、加奶等等,加了配料的价钱和不加的价钱肯定是不一样的。如果把一杯咖啡看做是一个类,那么我们可以用糖、奶这些配料来装饰。当然,这些配料会有很多种,也可以根据用户自己的喜好去添加,当然最终计算的金额肯定是不同的。
下面,就以这个场景为例,实现一个买咖啡计算金额的问题。
首先,需要创建一个咖啡类。
class Coffee {
// 接收一个价格参数,用于创建咖啡的原始价格
constructor(price) {
this.price = price;
}
// 获取价格
getPrice() {
return this.price;
}
}
这里,我们创建好了一个咖啡类,上线一个咖啡品种,我们可以new
一个咖啡类。比如,新上了拿铁咖啡,定价为 38 元。
// 拿铁原味一杯定价38
const latte = new Coffee(38);
const price = latte.getPrice();
console.log(price); // 38
那么,我们如何给咖啡添加辅料?如果用装饰器的思维来考虑,期望实现如下形式。
// 拿铁原味一杯定价38
const latte = new Coffee(38);
// 加点糖
latte.decorate('sugar');
// 加点奶
latte.decorate('milk');
// 计算最终总价
const price = latte.getPrice();
console.log(price);
在这里,你可以任意组合装饰器,来实现不同的逻辑组合。
那么,对于每一种辅料,我们都需要给他实现一遍它的价格计算的逻辑,在咖啡类中,即Coffee
的装饰器decorators
。为了保证每一个实例之间不相互影响,我们将装饰器decorators
挂载到类的实例方法上。
// 咖啡的装饰器
Coffee.decorators = {};
// 糖
Coffee.decorators.sugar = {
getPrice(price) {
return price + 2;
},
};
// 奶
Coffee.decorators.milk = {
getPrice(price) {
return price + 4;
},
};
// 折扣
Coffee.decorators.discount = {
getPrice(price, rate = 1) {
return price * rate;
},
};
// 人民币支付
Coffee.decorators.toRMB = {
getPrice(price) {
return `¥${price.toFixed(2)}`;
},
};
// 美元支付
Coffee.decorators.toDollar = {
getPrice(price) {
return `$${(price / 6.8).toFixed(2)}`;
},
};
到这里,装饰器就基本完成了,现在需要在调用装饰器的时候,告诉咖啡类去应用装饰器。
我们可以在装饰器调用的时候,将装饰器注册到Coffee
中的装饰器队列中,后续在获取价格的时候,在装饰器队列中去取到所有的装饰器计算汇总最终结果。
class Coffee {
constructor(price) {
this.price = price;
// 装饰器队列
this.ingredients = [];
}
getPrice() {
let price = this.price;
for (const [name, args] of this.ingredients) {
// 将上一个价格传入下一个装饰器,并获取下一个装饰器计算的价格
price = Coffee.decorators[name].getPrice(price, ...args);
}
// 汇总的价格
return price;
}
/**
* 注册装饰器
* @param name 名称
* @param args 装饰器需要的参数
*/
decorate(name, ...args) {
this.ingredients.push([name, args]);
}
}
此时,我们可以实现预期的调用形式。完整代码如下:
class Coffee {
constructor(price) {
this.price = price;
this.ingredients = [];
}
getPrice() {
let price = this.price;
for (const [name, args] of this.ingredients) {
price = Coffee.decorators[name].getPrice(price, ...args);
}
return price;
}
decorate(name, ...args) {
this.ingredients.push([name, args]);
}
}
Coffee.decorators = {};
Coffee.decorators.sugar = {
getPrice(price) {
return price + 2;
},
};
Coffee.decorators.milk = {
getPrice(price) {
return price + 4;
},
};
Coffee.decorators.discount = {
getPrice(price, rate = 1) {
return price * rate;
},
};
Coffee.decorators.toRMB = {
getPrice(price) {
return `¥${price.toFixed(2)}`;
},
};
Coffee.decorators.toDollar = {
getPrice(price) {
return `$${(price / 6.8).toFixed(2)}`;
},
};
// 拿铁原味一杯定价38
const latte = new Coffee(38);
// 加点糖
latte.decorate('sugar');
// 加点奶
latte.decorate('milk');
// 活动折扣,8折优惠
latte.decorate('discount', 0.8);
// 现金支付
latte.decorate('toRMB');
// 计算最终总价
const price = latte.getPrice();
console.log(price); // ¥35.20
本文完。😊
如果您觉得本文对您有用,欢迎捐赠或留言~
- 本博客所有文章除特别声明外,均可转载和分享,转载请注明出处!
- 本文地址:https://www.leevii.com/?p=2939