起因源于重构部分代码中,一个方法中有将近 20 个 if-else ,一下条件反射般的想起「策略模式」。
定义
策略模式(Strategy) :定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以互相替换。策略模式让算法的变化,不会影响到使用算法的客户。
实现
//策略接口
public interface Strategy {
void algorithm();
}
// Context 环境类(上下文类)
public class Context {
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
private Strategy strategy;
public void method(){
strategy.algorithm();
}
}
//具体策略实现类
public class StrategyA implements Strategy {
@Override
public void algorithm() {
System.out.println("A 逻辑");
}
}
public class StrategyB implements Strategy {
@Override
public void algorithm() {
System.out.println("B逻辑");
}
}
//客户端调用
public class Client {
public static void main(String[] args) {
Context context=new Context();
context.setStrategy(new StrategyA());
context.method();
}
}
//输出结果: A 逻辑
实战
目前在重构一个页面,涉及到后台接口,在实现类里面有十几个 if-else 的判断,一上来就很上头的想着使用策略模式修改一下。但是仔细看了下代码,觉得和策略模式差不多。但是除了满屏的 if-else
**因为每个 if 下实现的代码较为复杂,所以这十几个 if-else 都对应了一个类(是不是和具体实现类一样),对应上面的图只是差了一个策略接口和策略上下文,但是主要的策略类都实现了,并且是不同的类,并不是代码直接写在 if-else 下面的 **
那其实相当于我假如使用策略模式重构这段代码,只需要增加两个类,一个策略接口和策略上下文。但是问题又来了:
主要是我想去掉 if-else 代码,那现在我接口一个 type 值,势必需要通过这个判断,然后再去调用对应的策略类。最后想到了一个方法,使用 Map 缓存一下,那么就不需要判断,直接查到对应的实现类。参看如下的伪代码:
// Strategy :策略接口;ContextStrategy:策略上下文;c.algorithm():具体调用策略实现类的方法。
/**
我事先把 map.put(type,typeImpl) 都保存在这个 map 中,传入 type 只需要查询 map 接口,就不用那么多的 if-else。
**/
public void method(){
Strategy s = map.get(type);
ContextStrategy c = ContextStrategy.setStrategy(s);
c.algorithm();
}
//那么就可以使用上面的三行代码去替代满屏的 if-else ;不过需要在类里里面定义好静态的 Map
总结
像我上面的重构代码,假设此时我新增一个逻辑判断:
-
原来的:新增一个if-else判断,然后新增对应的逻辑处理代码
-
重构之后:新增一个静态map的 key-value,然后新增对应的逻辑处理代码
当然我们这里的 Map 缓存也可以换成配置文件;但是 Map 的优点是,我存放的时候就直接存放对象,那我用的时候就直接使用即可。配置文件的话,还得需要反射去创建对象然后才能使用。
if-else | 策略 | |
---|---|---|
优点 | 1.当判断很少的时候,可以使用这个;2.使用 if-else 大家都看得懂,实现没有难度 | 各个策略类互不影响,我修改、添加只需要针对一个类,而完全不用担心影响到其他逻辑。比如你测试的时候也可也单独测试,就很好 |
缺点 | 当判断很多的时候,很难维护,特别是全屏的if-else,如果需要改动、添加需求,最终只能老老实实的往后添加if-else,属于超强耦合了。 | if-else 越多,策略类就越多,类膨胀后,也难维护 |
参考
使用 Map 缓存的想法来源:设计模式之美
《大话涉及模式》