java设计模式之策略模式
介绍
- 意图: 定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。
- 解决问题: 在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护(简而言之消除if分支过多问题)
- 指导思想
开闭原则、迪米特法则、依赖倒置原则
问题场景
公司有节日活动,为每个员工分发奖金。经理(及以上)发5000,普通员工2000。
不使用策略模式
// 经理
String manage = "manage";
// 普通员工
String clerk = "clerk";
Scanner scanner = new Scanner(System.in);
String input = scanner.next();
if(input.equals(manage)){
System.out.println("经理,得5000奖金");
}else if(input.equals(clerk)){
System.out.println("其他员工,2000元奖金");
}
这样做把具体的活动奖金分发算法暴露给调用方,造成维护困难。
此时老板说,活动不发奖金了,改成聚餐,送购物卡等等。你要再修改复杂的逻辑。
第一次优化版
针对上述问题,活动奖金分发算法不再暴露给调用方,封装到统一的接口。并编写对应实现类,代码如下:
- 奖金分发策略接口
public interface BonusStrategy{
/**
* 奖金分发具体策略
*/
void bonusDistribution();
}
- 一等奖(经理级以上)具体奖励策略
public class FirstPrize implements BonusStrategy{
@Override
public void bonusDistribution(){
System.out.println("经理,一等奖,5000元");
}
}
3 .二等奖(普通员工)具体奖励策略
public class FirstPrize implements BonusStrategy{
@Override
public void bonusDistribution(){
System.out.println("经理,一等奖,5000元");
}
}
- 调用方代码:
public static void main(String[] args){
// 经理
String manage = "manage";
String clerk = "clerk";
Scanner scanner = new Scanner(System.in);
BonusStrategy bonusStrategy = null;
System.out.println("输入你的岗位");
String input = scanner.next();
if(input.equals(manage)){
bonusStrategy = new FirstPrize();
}else if(input.equals(clerk)){
bonusStrategy = new SecondPrize();
}
bonusStrategy.bonusDistribution();
}
- 此时老板发炎,经理不再是奖励5000元了,是送一辆车,只需要更改
FirstPrize.java
即可,调用方代码无需更改。 - but,老板又发炎了,在经理之上有新增一档特等奖,用于奖励特别优秀的经理,奖金一万。。此时代码如下。
- 新增一个特等奖策略,并实现奖金策略接口
public class GrandPrize implements BonusStrategy{
@Override
public void bonusDistribution(){
System.out.println("特等奖,奖励一万");
}
}
- 调用方也要做修改,主要新增一个if分支
public static void main(String[] args){
// 经理
String manage = "manage";
String clerk = "clerk";
String greatGod = "greatGod";
Scanner scanner = new Scanner(System.in);
BonusStrategy bonusStrategy = null;
System.out.println("输入你的岗位");
String input = scanner.next();
if(input.equals(manage)){
bonusStrategy = new FirstPrize();
}else if(input.equals(clerk)){
bonusStrategy = new SecondPrize();
}else if(input.equals(greatGod)){
bonusStrategy = new GrandPrize();
}
bonusStrategy.bonusDistribution();
}
* 此时我们改动了调用方代码,添加if分支,耦合度过高。
第二次优化版
上述方法已经实现了算法内容的透明性,一定程度上实现了解耦,但策略数量增加时,客户端还是要添加if分支实现相应的修改。
- 创建静态工厂
如果具体策略实现类交由客户端指定,那么if分支就一直会存在,最好的办法是结合静态工厂,把对象创建交由工厂类来完成。
static final Map<String,BonusStrategy> STRATEGY_MAP = new HashMap();
static {
STRATEGY_MAP.put("manage",new FirstPrize());
STRATEGY_MAP.put("clerk",new SecondPrize());
STRATEGY_MAP.put("greatGod",new GrandPrize());
}
/**
* 获取具体策略
* @param post
* @return
*/
public static BonusStrategy getStrategy(String post){
return STRATEGY_MAP.get(post);
}
- 消灭客户端(调用方)的if分支
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
BonusStrategy bonusStrategy = null;
System.out.println("输入你的岗位");
String input = scanner.next();
BonusStrategy strategy = StrategyFactory.getStrategy(input);
strategy.bonusDistribution();
}
此时,如果业务有变,我们的系统会如何应对呢?
- 修改奖金分发算法:只需修改对应的策略实现类。
- 增加奖金分发策略:新增一个策略实现策略接口,修改静态工厂在map添加新策略。
两种情况客户端代码都不用更改,也不存在if分支。
最终版
上述方法已经最大程度的降低耦合度,但还是有瑕疵,工厂接收的参数是string类型,存在硬编码问题。可以进一步优化。
总结
- 策略模式主要目的是封装算法,使之对客户端透明,消灭if分支。降低程序耦合度。
- 策略模式很少单独使用,一般结合工厂模式、享元模式等。