问题场景:
在代码编写的过程中,我们经常会遇到多个if-else堆叠的情况,这个时候对代码的维护和整理就需要花费很长的时间.那么我们应该怎么样去优化它呢.
比如下面的代码:
@interface ActionType {
int ACTION_1 = 1;
int ACTION_2 = 2;
int ACTION_3 = 3;
int ACTION_4 = 4;
}
public void handleAction(@ActionType int type) {
// 针对不同的type来调用不同的方法
if (type == ActionType.ACTION_1) {
handleAction1();
} else if (type == ActionType.ACTION_2) {
handleAction2();
} else if (type == ActionType.ACTION_3) {
handleAction3();
} else if (type == ActionType.ACTION_4) {
handleAction4();
}
}
public void handleAction1(){
System.out.println("action 1 executed!");
}
public void handleAction2(){
System.out.println("action 2 executed!");
}
public void handleAction3(){
System.out.println("action 3 executed!");
}
public void handleAction4(){
System.out.println("action 4 executed!");
}
我们需要针对不同的actiontype去进行不同的action的处理. 这段代码在后续维护的时候, 如果需要增加对应的type,那么就需要在handleAction方法中去做对应的if-else添加, 既不利于维护,同时,也会增加我们的圈复杂度,那么这段代码应该怎么去重构呢? 提供三个解决的思路:
- 利用策略模式进行重构
- 利用l表驱动的方式进行重构
- 利用责任链模式进行重构
利用策略模式进行重构:
策略模式的具体定义就不说了. 我们首先想到,可以把4个不同的处理,抽象成对应的策略:
public interface Strategy {
public void handle();
}
class Strategy1 implements Strategy {
@Override
public void handle() {
System.out.println("action 1 executed!");
}
}
public class Strategy2 implements Strategy{
@Override
public void handle() {
System.out.println("action 2 executed!");
}
}
public class Strategy3 implements Strategy{
@Override
public void handle() {
System.out.println("action 3 executed!");
}
}
public class Strategy4 implements Strategy{
@Override
public void handle() {
System.out.println("action 4 executed!");
}
}
好,那我们要怎么来获取到这个策略呢, 首先想到,肯定是通过对应的actiontype来获取到对应的策略. 那么我们可以使用工厂模式,来创建对应的策略:
class StrategyFactory {
private static StrategyFactory factory = new StrategyFactory();
private static Map map = new HashMap<Main.ActionType, Strategy>();
static {
map.put(Main.ActionType.ACTION_1, new Strategy1());
map.put(Main.ActionType.ACTION_2, new Strategy2());
map.put(Main.ActionType.ACTION_3, new Strategy3());
map.put(Main.ActionType.ACTION_4, new Strategy4());
}
private StrategyFactory() {}
public static StrategyFactory getInstance() {return factory;}
public Strategy create(@Main.ActionType int type){
return (Strategy)map.get(type);
}
}
那既然我们已经创建了工厂,那么就可以传入type,创建对应的策略来进行处理,那么最后的调用方法就变成了:
public void handleAction(@ActionType int type) {
StrategyFactory.getInstance().create(ActionType.ACTION_2).handle();
}
看,这样就明显的减少了if-else的调用,并且也易于理解,但是这样也有另外一个问题: 每次添加策略都需要添加策略类, 并且需要对factory的如果获取策略也比较熟悉, 那么有没有更简单的方法呢? 就到了我们后面需要说的:
表驱动的方式进行重构
通过lambda表达式,我们可以知道, 只有一个方法的类,创建实例时,可以通过lambda表达式进行简化,所以我们可以将strategy抽象成一个方法 :
interface Handler {
void handle();
}
和上面的策略模式完全一样,但是在第二步,就需要发生变化了, 因为我们不再使用工厂模式去进行策略的选取,我们采用lambda表达式来直接创建对应的内部类:
static Map<Integer,Handler> map = new HashMap<>();
static {
map.put(ActionType.ACTION_1, () -> System.out.println("action 1 executed!"));
map.put(ActionType.ACTION_2, () -> System.out.println("action 2 executed!"));
map.put(ActionType.ACTION_3, () -> System.out.println("action 3 executed!"));
map.put(ActionType.ACTION_4, () -> System.out.println("action 4 executed!"));
}
那这样的话,后面我们就可以很容易的获取到对应的策略,拿到策略后就可以直接进行处理,这个时候的处理函数代码为:
public void handleAction(@ActionType int type) {
map.get(type).handle();
}
这个方式和策略模式其实大同小异,但是因为通过lambda表达式简化了策略的创建,所以工厂的模式也可以不再使用.如果策略比较单一,却是完全可以采用这个方式.
责任链的方式进行重构
责任链的方式,首先需要确定的是当前处理的是哪种类型:
static abstract class Handler {
abstract boolean canHandle(@ActionType int type);
abstract void handle(int type);
}
static class Hander1 extends Handler {
@Override
boolean canHandle(int type) {
return type == ActionType.ACTION_1;
}
@Override
void handle(int type) {
if (canHandle(type))
System.out.println("action 1 executed!");
}
}
所以,此时,我们只需要将对应的策略添加到一条链上,此时我们可以使用map来模拟一条责任链:
static Map<Integer,Handler> map = new HashMap<>();
static {
map.put(ActionType.ACTION_1, new Hander1());
map.put(ActionType.ACTION_2, new Hander2());
map.put(ActionType.ACTION_3, new Hander3());
map.put(ActionType.ACTION_4, new Hander4());
}
那我们需要处理的时候,只需要进行一整条链的处理,那么能够处理的自然就会进行处理了:
public void handleAction(@ActionType int type) {
map.forEach((k, v) -> {
v.handle(type);
});
}
这个时候,对应的handler就会handetype了.
总结:
代码需要及时的进行重构,不然就会导致破窗效应,bad smell的代码越来越多, 会导致整个工程更加难以运用. 所以我们要及时的对代码进行整理和重构.