策略模式
# 概念
定义一组算法,将 ** 每个算法都封装起来,** 并且使它们之间可以互换。
# 使用场景
- 多个类只有在算法或行为上稍有不同的场景。
- 算法需要自由切换的场景。
- 需要屏蔽算法规则的场景。
- 具体策略数量超过 4 个,则需要考虑使用混合模式。
# 角色
- 抽象策略(Strategy)类:定义了一个公共接口,各种不同的算法以不同的方式实现这个接口,环境角色使用这个接口调用不同的算法,一般使用接口或抽象类实现。
- 具体策略(Concrete Strategy)类:实现了抽象策略定义的接口,提供具体的算法实现。
- 环境(Context)类:持有一个策略类的引用,最终给客户端调用。
# 代码示例
# 需求
要做一个营销,需要用户参与一个活动,然后完成一系列的任务,最后可以得到一些奖励作为回报。活动的奖励包含美团外卖、酒旅和美食等多种品类券。
# 优化前代码
// 奖励服务
class RewardService {
// 外部服务
private WaimaiService waimaiService;
private HotelService hotelService;
private FoodService foodService;
// 使用对入参的条件判断进行发奖
public void issueReward(String rewardType, Object... params) {
if ("Waimai".equals(rewardType)) {
WaimaiRequest request = new WaimaiRequest();
// 构建入参
request.setWaimaiReq(params);
waimaiService.issueWaimai(request);
} else if ("Hotel".equals(rewardType)) {
HotelRequest request = new HotelRequest();
request.addHotelReq(params);
hotelService.sendPrize(request);
} else if ("Food".equals(rewardType)) {
FoodRequest request = new FoodRequest(params);
foodService.getCoupon(request);
} else {
throw new IllegalArgumentException("rewardType error!");
}
}
}
存在问题:
- 不符合开闭原则,可以预见,如果后续新增品类券的话,需要直接修改主干代码,而我们提倡代码应该是对修改封闭的;
- 不符合迪米特法则,发奖逻辑和各个下游接口高度耦合,这导致接口的改变将直接影响到代码的组织,使得代码的可维护性降低。
# 优化后代码
策略模式 + 适配器模式
// 策略接口
interface Strategy {
void issue(Object... params);
}
// 外卖策略
class Waimai implements Strategy {
private WaimaiService waimaiService;
@Override
public void issue(Object... params) {
WaimaiRequest request = new WaimaiRequest();
// 构建入参
request.setWaimaiReq(params);
waimaiService.issueWaimai(request);
}
}
// 酒旅策略
class Hotel implements Strategy {
private HotelService hotelService;
@Override
public void issue(Object... params) {
HotelRequest request = new HotelRequest();
request.addHotelReq(params);
hotelService.sendPrize(request);
}
}
// 美食策略
class Food implements Strategy {
private FoodService foodService;
@Override
public void issue(Object... params) {
FoodRequest request = new FoodRequest(params);
foodService.payCoupon(request);
}
}
// 使用分支判断获取的策略上下文
class StrategyContext {
public static Strategy getStrategy(String rewardType) {
switch (rewardType) {
case "Waimai":
return new Waimai();
case "Hotel":
return new Hotel();
case "Food":
return new Food();
default:
throw new IllegalArgumentException("rewardType error!");
}
}
}
// 优化后的策略服务
class RewardService {
public void issueReward(String rewardType, Object... params) {
Strategy strategy = StrategyContext.getStrategy(rewardType);
strategy.issue(params);
}
}
注意
这里只表达了有无使用策略模式的区别,并非最优代码,代码仍可进一步优化,具体优化细节可参考参考资料。
# 总结
从原代码可知,对于各品类的操作宏观上都是 入参 -> 放发
,但微观的代码上又不一样。
// 外卖
WaimaiRequest request = new WaimaiRequest();
request.setWaimaiReq(params);
waimaiService.issueWaimai(request);
// 酒店
HotelRequest request = new HotelRequest();
request.addHotelReq(params);
hotelService.sendPrize(request);
// 美食
FoodRequest request = new FoodRequest(params);
foodService.getCoupon(request);
# 参考资料
- 嘉凯。杨柳。设计模式二三事 [EB/OL]. [2022-09-07]. https://tech.meituan.com/2022/03/10/interesting-talk-about-design-patterns.html (opens new window).
- baiyuxuan. 在 SpringBoot 中优雅地实现策略模式 [EB/OL]. 2021-11-28 [2023-9-12]. https://juejin.cn/post/7035414939657306126.
上次更新: 2023/09/13, 13:25:19