重构重复if语句的三个方式

问题场景:

在代码编写的过程中,我们经常会遇到多个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添加, 既不利于维护,同时,也会增加我们的圈复杂度,那么这段代码应该怎么去重构呢? 提供三个解决的思路:

  1. 利用策略模式进行重构
  2. 利用l表驱动的方式进行重构
  3. 利用责任链模式进行重构

利用策略模式进行重构:

策略模式的具体定义就不说了. 我们首先想到,可以把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的代码越来越多, 会导致整个工程更加难以运用. 所以我们要及时的对代码进行整理和重构.