现在有这样的场景:
固定金额:
固定金额是指员工不能体现的金额,这部分金额只能用来特定消费,即员工日常必须的消费。如:公司食堂内吃饭、理发、健身等。
自由金额:
自由金额是可以体现的,当然也可以用于消费。每个月初,总部会为每个员工的IC卡中打入固定的金额,然后提倡大家在集团内的商店消费。
策略设计
在实际的系统开发中,架构设计采用的是一张IC卡绑定两个账户:固定账户和自由账户。现对其设计两种扣款策略
扣款策略一:
该类型的扣款策略会对IC卡上的两个金额产生影响,计算公式如下:
IC卡固定余额 = IC卡现有固定金额 - 交易金额/2
IC卡自由金额 = IC卡现有自由金额 - 交易金额/2
也就是说,该类型的消费分别在固定金额和自由金额上各扣除一半。它适用于固定消费场景例如吃饭、理发等情况下的扣款,这么做是为了防止乱请客,你请别人吃饭时自己也要出一半。
扣款策略二:
全部从自由金额上扣除,由于集团内的各种消费规则、服务非常齐全,而且必市面价格略低,员工还是很乐意到这里消费的。
类图设计
每个IC卡有三个属性,分别是IC卡号码cardNo、固定金额steadyMoney、自由金额freeMoney。
交易信息Trade类,负责记录每一笔交易,有两个属性:交易编号tradeNo、交易金额amount。
这两个是实体,类图就自行设计。
扣款策略接口:
public interface IDeduction {
// 扣款,提供交易和卡信息,进行扣款,并返回扣款是否成功
boolean exec(Card card, Trade trade);
}
扣款策略接口一:
public class SteadyDeduction implements IDeduction {
// 固定余额和自由金额各扣除50%
@Override
public boolean exec(Card card, Trade trade) {
int halfMoney = (int)Math.rint(trade.getAmount() / 2.0);
card.setFreeMoney(card.getSteadyMoney() - halfMoney);
card.setSteadyMoney(card.getSteadyMoney() - halfMoney);
return true;
}
}
扣款策略接口二:
public class FreeDeduction implements IDeduction {
// 自由扣款
@Override
public boolean exec(Card card, Trade trade) {
// 直接从自由余额中扣除
card.setFreeMoney(card.getFreeMoney() - trade.getAmount());
return true;
}
}
扣款策略的封装:
/**
* Created by CeaM on 2021/1/22 0022 21:51
* 扣款策略的封装,封装上层父类
*/
public class DeductionContext {
// 扣款策略
private IDeduction deduction = null;
// 构造函数传递策略
public DeductionContext(IDeduction deduction) {
this.deduction = deduction;
}
// 执行扣款
public boolean exec(Card card, Trade trade){
return this.deduction.exec(card, trade);
}
}
总结
策略模式的优点
1、算法可以自由切换
2、避免使用多重条件判断
3、扩展性良好
策略模式的缺点
1、策略类数量增多
2、所有的策略类都需要对外暴露
上层模块(目前的架构)必须知道有哪些策略,然后才能决定使用哪个策略,这与迪米法则是相违背的,我只是想使用了一个策略,凭什么就要了解这个策略呢?那要你的封装类还有什么意义?这是策略模式的一个缺点,幸运的是,我们可以使用其它模式来修正这个缺陷,如工厂模式、代理模式或享元模式。
策略模式使用场景
1、多个类只有在算法或行为上稍有不同的场景
2、算法需要自由切换的场景
3、需要屏蔽掉算法规则的场景
策略模式的注意事项
策略家族的具体策略数量超过4个,则需要考虑混合模式,解决策略类膨胀和对外暴露的问题,否则日后的系统维护工作谁都不想接。