定义
策略模式定义了一系列算法分别封装起来,让它们之间可以相互替换,此模式让算法的变化独立于使用算法的的客户。
优点
- 利用组合、委托等技术,可以有效的避免多重条件选择语句
- 提供了对开放-封闭原则的完美支持,将算法封装在独立的strategy中,使得它们易于切换扩展
- 利用组合和委托来让Context拥有执行算法的能力,这也是继承的一种更轻便的代替方案
使用
假如要实现这样一个业务:商城里每个产品都有原价为 originalPrice,但是存在打折促销的情况
。规则为:
- 部分产品不参与活动:
default
状态,原价出售。 - 部分产品已预售:pre-sale 状态,原价基础上打八折。
- 部分产品参与促销活动:promotion 状态,如果原价不高于
100
,九折出售;如果原价高于100
,则减20 元。
不考虑任何设计模式的写法可能用 if-else
多次判断来计算价格。
function getPrice(originalPrice, status) {
if (status === "default") {
return originalPrice;
}
if (status === "pre-sale") {
return originalPrice * 0.8;
}
if (status === "promotion") {
if (origialPrice <= 100) {
return origialPrice * 0.9;originalPrice
} else {
return originalPrice - 20;
}
}
}
虽然能实现,但存在问题:
第一,它违反了单一职责原则(每个类或者函数都应该有一个单一的功能,并且该功能应该由这个类或者函数完全封装起来)。函数 getPrice
做了太多的事情,如果一个条件出现 bug
,整个函数就会崩溃(一处有问题,所有功能不能实现)
第二,违反了开闭原则(对扩展开放,对修改关闭)。
比如:新增一个活动 “疯狂星期四”crazy-thursday,规则为:
- 价格低于或等于 100 元的产品以 20% 的折扣出售。
- 价格高于 100 元但低于 200 元的产品将减少 20 元。
- 价格高于或等于 200 元的产品将减少 20 元。
那么,getPrice
函数中需添加以下判断:
if (status === "crazy-thursday") {
if (origialPrice >= 100 && originalPrice < 200) {
return origialPrice - 20;
} else if (originalPrice >= 200) {
return originalPrice - 50;
} else {
return originalPrice * 0.8;
}
}
此时,getPrice函数
代码很难应对变化的需求,每次修改已有的功能(每当增加或减少折扣时,都需要更改函数getPrice
)很容易出现新的错误,而且还会使得 getPrice
越来越臃肿。
那么,可以同策略模式进行优化:可以使用映射策略 Strategies 而不是冗长的 if-else
来存储对应关系,并拆分这个函数 getPrice
以减少臃肿。
function defaultPrice(origialPrice) {
return origialPrice;
}
function preSalePrice(origialPrice) {
return originalPrice * 0.8;
}
function promotionPrice(origialPrice) {
if (origialPrice <= 100) {
return origialPrice * 0.9;
} else {
return originalPrice - 20;
}
}
function crazyhursdayPrice(origialPrice) {
if (origialPrice >= 100 && originalPrice < 200) {
return origialPrice - 20;
} else if (originalPrice >= 200) {
return originalPrice - 50;
} else {
return originalPrice * 0.8;
}
}
const priceStrategies = {
"pre-sale": preSalePrice,
"promotion": promotionPrice,
"black-friday": blackFridayPrice,
"default": defaultPrice,
};
function getPrice(originalPrice, status) {
return priceStrategies[status](originalPrice);
}
缺点
- 会在程序中增加许多策略类或者策略对象
- 要使用策略模式,必须了解所有的strategy,必须了解各个strategy之间的不同点,这样才能选择一个合适的strategy