策略+工廠+反射記錄一次switch代碼簡化過程

遇到的問題:一張記錄表,記錄了10個業務的字段,一個入參type說明了要修改哪個字段,最初是通過switch(type)case…來做的

但是涉及這樣子的判斷多了,每次都要不斷的switch,並且case裏面不同方法有不同的處理,一個公共的switch並不能夠滿足

又不能在每一個方法中都寫一個10個case的switch,代碼太過臃腫了,因而產生了今天的記錄,當然更加臃腫的if else更加能夠簡化啦

利用策略+工廠+反射,簡化臃腫的switch

一、常量映射類

  • 現在定義一個常量類,表示了type和方法的映射
public enum LeaveTypeEnum {
    CASUAL_LEAVE("事假","setCasualLeave","setClBackup","getCasualLeave","getClBackup","setCasual","getCasual"),
    SICK_LEAVE("帶薪病假","setSickLeave","setSlBackup","getSickLeave","getSlBackup","setSick","getSick"),
    SICK_LEAVE_NORMAL("普通病假","setSickLeaveNormal","setSlnBackup","getSickLeaveNormal","getSlnBackup", "setSickNormal", "getSickNormal"),
    MARRIAGE_LEAVE("婚假", "setMarriageLeave", "setMlBackup", "getMarriageLeave", "getMlBackup", "setMarriage", "getMarriage"),
    FUNERAL_LEAVE("喪假", "setFuneralLeave", "setFlBackup", "getFuneralLeave", "getFlBackup", "setFuneral", "getFuneral"),
    MATERNITY_LEAVE("產假","setMaternityLeave","setMnlBackup","getMaternityLeave","getMnlBackup","setMaternity","getMaternity"),
    MATERNITY_LEAVE_1("產假-難產假", "setMaternityLeave", "setMnlBackup", "getMaternityLeave", "getMnlBackup", "", ""),
    MATERNITY_LEAVE_2("產假-多胞胎假","setMaternityLeave","setMnlBackup","getMaternityLeave","getMnlBackup","",""),
    MATERNITY_LEAVE_3("產假-哺乳假", "setMaternityLeave", "setMnlBackup", "getMaternityLeave","getMnlBackup","",""),
    MATERNITY_LEAVE_4("產假-產檢假", "setMaternityLeave", "setMnlBackup", "getMaternityLeave", "getMnlBackup", "setMaterPaternity","getMaterPaternity"),
    MATERNITY_LEAVE_5("產假-流產假", "setMaternityLeave", "setMnlBackup", "getMaternityLeave", "getMnlBackup", "", ""),
    PATERNITY_LEAVE("陪產假","setPaternityLeave","setPnlBackup","getPaternityLeave","getPnlBackup","setMaterPaternity","getMaterPaternity"),
    ANNUAL_LEAVE("年假", "setAnnualLeave", "setAlBackup", "getAnnualLeave", "getAlBackup", "setAnnual", "getAnnual");


    private String name;
    private String setMethod;
    private String setSubMethod;
    private String getMethod;
    private String getSubMethod;
    private String setCalMethod;
    private String getCalMethod;

    LeaveTypeEnum(String name,String setMethod,String setSubMethod,String getMethod,String getSubMethod,String setCalMethod,String getCalMethod) {
        this.name = name;
        this.setMethod = setMethod;
        this.setSubMethod = setSubMethod;
        this.getMethod = getMethod;
        this.getSubMethod = getSubMethod;
        this.setCalMethod = setCalMethod;
        this.getCalMethod = getCalMethod;
    }

    public String getName() {
        return name;
    }

    public static String getMethodByNameForSet(String name) {
        for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
            if (leaveType.name.equals(name)) {
                return leaveType.setMethod;
            }
        }
        return null;
    }

    public static String getSubMethodByNameForSet(String name) {
        for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
            if (leaveType.name.equals(name)) {
                return leaveType.setSubMethod;
            }
        }
        return null;
    }

    public static String getMethodByNameForGet(String name) {
        for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
            if (leaveType.name.equals(name)) {
                return leaveType.getMethod;
            }
        }
        return null;
    }

    public static String getSubMethodByNameForGet(String name) {
        for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
            if (leaveType.name.equals(name)) {
                return leaveType.getSubMethod;
            }
        }
        return null;
    }

    public static String getMethodByNameCalForSet(String name) {
        for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
            if (leaveType.name.equals(name)) {
                return leaveType.setCalMethod;
            }
        }
        return null;
    }

    public static String getMethodByNameCalForGet(String name) {
        for (LeaveTypeEnum leaveType : LeaveTypeEnum.values()) {
            if (leaveType.name.equals(name)) {
                return leaveType.getCalMethod;
            }
        }
        return null;
    }
}
  • 以上一個映射類和反射就能夠解決問題,但是爲了更簡化並且利用上一些設計模式,就嘗試了策略+工廠

二、 接口

public interface InvokeStrategy {
    Double getByColumn(String type,String leaveType,String methodName,Object obj);

三、接口實現類

  @Slf4j
  public class UserVacationInvokeHandler implements InvokeStrategy {
      @Override
      public Double getByColumn(String type,String leaveType,String methodName, Object obj) {
          UserVacation userVacation = (UserVacation) obj;
          try {
              Method method = UserVacation.class.getDeclaredMethod(methodName);
              Invokable<UserVacation, Object> invokable =
                      (Invokable<UserVacation, Object>) Invokable.from(method);
              Double invoke = (Double) invokable.invoke(userVacation);
              return invoke;
          }catch (Exception e ){
              log.error("方法調用失敗,type:{},method:{}",type,methodName);
          }
          return null;
      }
  
      @Override
      public void setByColumn(String type, String leaveType,String methodName,Double param, Object obj) {
          UserVacation userVacation = (UserVacation) obj;
          try {
              Method method = UserVacation.class.getDeclaredMethod(methodName,Double.class);
              Invokable<UserVacation, Object> invokable =
                      (Invokable<UserVacation, Object>) Invokable.from(method);
             invokable.invoke(userVacation,param);
          }catch (Exception e ){
              log.error("方法調用失敗,type:{},method:{}",type,methodName);
          }
      }
  }

四、策略工廠

public class InvokeStrategyFactory {
    private static InvokeStrategyFactory factory = new InvokeStrategyFactory();
    private InvokeStrategyFactory(){
    }
    private static Map<String,InvokeStrategy> strategyMap = new HashMap<>();
    static{
        strategyMap.put("UserVacation", new UserVacationInvokeHandler());
        strategyMap.put("UserVacationCal", new UserVacationCalInvokeHandler());
    }
    public InvokeStrategy creator(String type){
        return strategyMap.get(type);
    }
    public static InvokeStrategyFactory getInstance(){
        return factory;
    }

}

五、策略上下文

public class InvokeStrategyContext {
    private InvokeStrategy strategy;

    public Double getByColumn(String type, String leaveType, String methodName,Object obj) {
        strategy = InvokeStrategyFactory.getInstance().creator(type);
        return strategy.getByColumn(type, leaveType, methodName,obj);
    }

    public void setByColumn(String type, String leaveType,String methodName, Double param, Object obj) {
        strategy = InvokeStrategyFactory.getInstance().creator(type);
        strategy.setByColumn(type, leaveType,methodName, param, obj);
    }

    public InvokeStrategy getStrategy() {
        return strategy;
    }

    public void setStrategy(InvokeStrategy strategy) {
        this.strategy = strategy;
    }
}

六、使用

        InvokeStrategyContext context = new InvokeStrategyContext();
        Lock lock = new ReentrantLock();
        lock.lock();
        try {
        //只需這兩行,就替換掉了一大堆的switch,並且能夠輕鬆的進行中間操作,不再臃腫
            Double result = context.getByColumn("UserVacation", dto.getType(), methodOld, userVacation);
            context.setByColumn("UserVacation", dto.getLeaveType(), setMethod, result, userVacation);
        } catch (Exception e) {
            lock.unlock();
            log.error("更新uservation出現異常");
        } finally {
            lock.unlock();
        }

七、互聯網上也不乏有一些方法

  • 傳統反射
/***
 *定義每種類型所對應的方法
*/
public class ReflectTest {
    public void methodOne() {
        System.out.println("one");
    }

    public void methodTwo() {
        System.out.println("two");
    }

    public void methodThree() {
        System.out.println("three");
    }

    public void methodFour() {
        System.out.println("four");
    }

}

 /***
     *
     * 通過反射,動態調用方法。採用了Guava的工具類。
     * */
    @Test
    public void testReflect() throws Exception {
        //首字母大寫,根據類型拼接方法
        String methodName = "method" + LOWER_CAMEL.to(UPPER_CAMEL, input);
        Method method = ReflectTest.class.getDeclaredMethod(methodName);
        Invokable<ReflectTest, Object> invokable =
                (Invokable<ReflectTest, Object>) Invokable.from(method);
        invokable.invoke(new ReflectTest());
    }
  • jdk8 lambda表達式
    public void testJava8() {
        Map<String, Consumer<ReflectTest>> functionMap = Maps.newHashMap();
        functionMap.put("one", ReflectTest::methodOne);
        functionMap.put("two", ReflectTest::methodTwo);
        functionMap.put("three", ReflectTest::methodThree);
        functionMap.put("four", ReflectTest::methodThree);
        functionMap.get(input).accept(new ReflectTest());
    }
  • 枚舉
public enum EnumTest {


    ONE("one") {
        @Override
        public void apply() {
            System.out.println("one");
        }
    },
    TWO("two") {
        @Override
        public void apply() {
            System.out.println("two");
        }
    }, THREE("three") {
        @Override
        public void apply() {
            System.out.println("three");
        }
    }, FOUR("four") {
        @Override
        public void apply() {
            System.out.println("four");
        }
    };

    public abstract void apply();

    private String type;

    EnumTest(String type) {
        this.type = type;
    }

    public String getType() {
        return type;
    }

}
  // 枚舉測試
 @Test
    public void testEnum() {
        EnumTest.valueOf(input.toUpperCase()).apply();
    }

  • 我上面的方法其實結合了枚舉和傳統的反射,這邊還看到了一個jdk8的實現,想必大家一定很好奇

八、jdk8的改造

  • 以上利用consumer執行了一個method,不帶有返回值,我們執行set的方法的時候可以效法consumer,我們如果是get 怎麼辦呢?
  • 我們在使用Lambda表達式的時候,都會用到foreach和map兩種算子,foreach接納參數consumer,不帶有返回值,map接納參數function,可以有映射的值,這就引導我們意識到了function
//無返回參數
Map<String, Consumer<ReflectTest>> functionMap = Maps.newHashMap();
        functionMap.put("one", ReflectTest::methodOne);
        functionMap.put("two", ReflectTest::methodTwo);
        functionMap.put("three", ReflectTest::methodThree);
        functionMap.put("four", ReflectTest::methodThree);
        functionMap.get(input).accept(new ReflectTest());
//帶返回參數

        Map<String, Function<ReflectTest,String>> map = Maps.newHashMap();
        map.put("one", ReflectTest::methodOne);
        map.put("two", ReflectTest::methodTwo);
        map.put("three", ReflectTest::methodThree);
        map.put("four", ReflectTest::methodThree);
        String apply = map.get("one").apply(new ReflectTest());
        System.out.println(apply);

//經測試,能夠滿足需求       
  • 那麼枚舉加傳統反射也可以通過以上lambda表達式的方式改造啦,一個全局的靜態的Map,接納了關於set和get的定義,在使用的時候調用即可
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章