引言:策略模式,顾名思义,就是:同一堆对象,不同的交流方式和语言内容,达到不同的目的。
比如:同一套零件,根据不同组装策略,形成最终不同的产品。
又或者:去年你年终奖是2个月薪水,今年你boss调整了发放年终奖的策略,最终你今年的年终奖变成了3个月薪水。
Design Pattern -- Strategy Pattern.png
场景
我们坐巴士,假如我是老板,好,我不想按照“上车两块钱,坐到你想吐”的策略去买单,我想要你在上下车都刷卡,然后按照你坐巴士的时间长短来计算你应该付多少钱,这样看起来貌似更加公平。
那么,怎么把这个新的策略实施下去呢?假如明年又要换一个收费政策呢?
分析与设计
根据不同的政策,来制定不同的刷牙和支付策略,并实际执行在每一辆巴士上面,于是我们有了文章开头的贴图--用“策略模式”。
首先,我们从场景中能够看出,一辆巴士上有了两个策略的更新换代:从“上车刷卡”到“上下车都刷卡”,从“支付2元”到“按照坐车时间收费”
1. 首先,建立角色对吧,巴士:
public class Bus {
/// 刷卡付费策略
private BusRecordStrategy busRecordStrategy;
public Bus() {
/// 默认使用旧的刷卡策略
this.busRecordStrategy = new OldBusRecordStrategy();
}
/// 策略模式精髓 : set 一个新的刷卡付费策略
public void setBusRecordStrategy(BusRecordStrategy busRecordStrategy) {
this.busRecordStrategy = busRecordStrategy;
}
public void getOn(String icCardId){
System.out.println(icCardId + " 已上车");
this.busRecordStrategy.recordGetOn(icCardId);
}
public void getOff(String icCardId){
System.out.println(icCardId + " 已下车");
this.busRecordStrategy.recordGetOff(icCardId);
}
}
OK, 当我们写完这个Bus
类时,大家应该已经可以看出,策略模式最精髓的部分了:那就是 动态配置和切换。新旧策略只要重新set一遍,逻辑就能完成替换。
2. 好,我们看看这个BusRecordStrategy
类,他是一个抽象类,并且不算特别抽象,为什么这么设计?因为我们不推荐一个和这么具体的业务(如:刷卡记录、付费等)相关的策略被设计得特别抽象(泛型什么的就不需要用到了),为了保证其策略变化幅度可控,并且策略种类有限且有效。
如果策略过多,并且你很想加方法加接口,那么这时,我们应该考虑拆分策略了,“单一职责”原则 不是开玩笑的。
public abstract class BusRecordStrategy {
protected BusPayStrategy payStrategy;
public BusRecordStrategy(BusPayStrategy payStrategy) {
this.payStrategy = payStrategy;
}
public abstract void recordGetOn(String icCardId);
public abstract void recordGetOff(String icCardId);
}
我们可以看到,这个抽象策略内部,又有一个BusPayStrategy
, 这个BusPayStrategy
主要负责计算乘客要扣多少钱的,我们可以像上面这样通过构造器传入,也可以通过在两个抽象方法recordGetOn
和recordGetOff
中传入(这个看大家的思路如何吧)
3. 好,我们看到这个金额计算策略接口BusPayStrategy
同样拥有具体的接口,根据乘客乘车记录IcCardRecord
来做相应的计算,最终输出一个金额。
public interface BusPayStrategy {
public double calculateBusPay(IcCardRecord icCardRecord);
}
public class IcCardRecord {
private String id;
private Date getOnTimeStamp;
private Date getOffTimeStamp;
...................
}
4. 这样一来,大体的结构就出来了,我们只需要根据不同的实际情况,编写不同的策略实现类,就可以完成迅速的逻辑替换了。
- 旧的刷卡策略:不会记录乘客信息,直接上车就扣费。
public class OldBusRecordStrategy extends BusRecordStrategy { public OldBusRecordStrategy() { super(new OldBusPayStrategy()); } @Override public void recordGetOn(String icCardId) { double cost = this.payStrategy.calculateBusPay(null); System.out.printf("%s扣费:%.2f元\n", icCardId,cost); } @Override public void recordGetOff(String icCardId) { } }
- 新的刷卡策略:上下车都会记录乘客的当时时刻,并且下车后扣费。
public class NewBusRecordStrategy extends BusRecordStrategy { private Map<String , IcCardRecord> passengerRecordMap; public NewBusRecordStrategy() { this(new NewBusPayStrategy()); this.passengerRecordMap = new HashMap<>(); } public NewBusRecordStrategy(BusPayStrategy payStrategy) { super(payStrategy); } @Override public void recordGetOn(String icCardId) { IcCardRecord icCardRecord = null; if (passengerRecordMap.containsKey(icCardId)) { icCardRecord = passengerRecordMap.get(icCardId); }else { icCardRecord = new IcCardRecord(); icCardRecord.setId(icCardId); } icCardRecord.setGetOnTimeStamp(new Date()); this.passengerRecordMap.put(icCardId,icCardRecord); } @Override public void recordGetOff(String icCardId) { IcCardRecord icCardRecord = passengerRecordMap.get(icCardId); if (icCardRecord!=null) { icCardRecord.setGetOffTimeStamp(new Date()); } double cost = this.payStrategy.calculateBusPay(icCardRecord); this.passengerRecordMap.remove(icCardId); System.out.printf("%s扣费:%.2f元\n", icCardId,cost); } }
- 旧的扣费策略:固定扣2元
public class OldBusPayStrategy implements BusPayStrategy { private final double BASIC_COST = 2.0; @Override public double calculateBusPay(IcCardRecord icCardRecord) { return BASIC_COST; } }
- 新的扣费策略:根据乘客上下车时间差计算费用
public class NewBusPayStrategy implements BusPayStrategy { @Override public double calculateBusPay(IcCardRecord icCardRecord) { long getOffTime = icCardRecord.getGetOffTimeStamp().getTime(); long getOnTime = icCardRecord.getGetOnTimeStamp().getTime(); long resSeconds = (getOffTime - getOnTime) / 1000; return 0.02 * resSeconds; } }
结果
最后,谢谢各位读者
网友评论