如何優雅地優化代碼中的的if else和switch

d70620c9e1ab319.jpg

引言

通常來講,隨着咱們項目的迭代以及業務的愈來愈複雜,項目中的分支判斷會原來越多。當項目中涉及到複雜的業務判斷或者分支邏輯時,咱們就須要考慮是否須要對項目進行重構了,或者if else和switch case是否可以知足當前項目的複雜度。ide

咱們舉一個簡單的例子,假如咱們是馬戲團的老闆,在訓練一些動物去作一些指令,剛開始很簡單,只訓練了一條狗,當狗握了一下手後,給她獎勵一些狗糧。這樣慢慢地小狗就學會了握手。優化

咱們先定義一條小狗對象,小狗作了某些事情(「握手」)後,能夠獲得一些獎勵ui

public class Dog {
    public void train(){
        System.out.println("握手");
    }
    public void getReward(){
        System.out.println("狗糧");
    }
}

定義馴獸師,經過train來訓練動物spa

public class Beast {
    /**
     * 訓練
     */
    public void train(Dog dog){
        /**
         * 狗狗作了一些事情
         */
        dog.train();
        /**
         * 狗狗獲得獎勵
         */
        dog.getReward();
    }
}

若是咱們只須要訓練一條動物,那麼相對來講比較簡單。可是後來馬戲團又引進了一頭獅子,須要訓練獅子鑽火圈,所以,爲了區分狗和獅子,咱們增長了一種類型區分訓練的動物是狗仍是獅子。設計

public class Lion {
    public void train(){
        System.out.println("鑽火圈");
    }
    public void getReward(){
        System.out.println("獲得一隻雞");
    }
}

從新修改Beast類,使其既能夠訓練小狗,又能夠訓練獅子code

public class Beast {
    
    public void train(Object animal, int type){
        if (type == 1){
            trainDog((Dog)animal);
        }else if (type == 2){
            trainLion((Lion)animal);
        }
    }
    
    /**
     * 訓練
     */
    public void trainDog(Dog dog){
        /**
         * 狗狗作了一些事情
         */
        dog.train();
        /**
         * 狗狗獲得獎勵
         */
        dog.getReward();
    }
    /**
     * 訓練
     */
    public void trainLion(Lion lion){
        /**
         * 狗狗作了一些事情
         */
        lion.train();
        /**
         * 狗狗獲得獎勵
         */
        lion.getReward();
    }
}

咱們經過type類型來區分訓練的動物類型,後來馬戲團引來了愈來愈多的動物,那咱們的type的取值會愈來愈多:1表示狗,2表示獅子,3表示貓,4表示老虎,5表示猴子...等等。並且隨着系統愈來愈複雜,咱們在訓練以前不一樣的動物還須要作不一樣的準備工做。固然,目前的系統只要增長if else或者switch case是能夠知足需求的,但這樣寫顯得不是太優雅,咱們但願找到一種比較優雅比較有設計感的方式來取代if else或者switch case對象

優化if else

在作優化以前,咱們須要先弄清楚咱們的目的。咱們是馬戲團的馴獸師,目的是訓練動物。blog

  • 目的:訓練動物作一些事情(doSomething)
  • 方式:經過獎勵(getReward)誘導動物進行訓練。

有了上面的目的以後,咱們就能夠定義一個模型animal接口

public interface IAnimal {

    /**
     * 獲取動物種類
     * @return
     */
    int getType();
    
    /**
     * 訓練動做
     */
    void train();
}

Animal只暴露兩個方法,其中doSomething是專門用來訓練動物的,至於如何訓練全都由子類實現。get

  • getType():用來區分不一樣的動物
  • train():訓練動物

而後咱們定義一個子類實現這個接口,用來具體化如何訓練動物

public abstract class AbsTrainAnimal implements IAnimal{

    /**
     * 訓練前須要作的準備
     */
    abstract void beforeTrain();

    /**
     * 訓練後須要作的準備
     */
    abstract void afterTrain();

    /**
     * 訓練出現異常須要作的
     */
    abstract void exceptionTrain(Throwable throwable);

    /**
     * 具體訓練
     */
    abstract void doSomething();
    
    /**
     * 訓練動做
     */
    @Override
    public final void train() {
        try {
            beforeTrain();
            doSomething();
            afterTrain();
        }catch (Throwable throwable){
            exceptionTrain(throwable);
        }
    }
}

咱們定義了一個抽象類用來實現IAnimal接口,做爲全部動物訓練的基類。其中實現的接口train使用了final進行了限制,防止子類對其進行覆蓋操做。

在AbsTrainAnimal中,咱們對train()進行了各類功能的細化

  • doSomething:具體訓練的內容
  • beforeTrain:訓練以前須要作的一些準備
  • afterTrain:訓練以後須要作的事情
  • exceptionTrain:訓練中發生意外應該如何處理

由於全部動物的以上四個方法可能都不相同,因此咱們聲明爲abstract方法,方便子類本身實現。基於以上設計,咱們就能夠定義一個Dog類,對其進行訓練。

public class Dog extends AbsTrainAnimal {
    /**
     * 訓練前須要作的準備
     */
    @Override
    void beforeTrain() {
        System.out.println("撫摸額頭以示鼓勵");
    }

    /**
     * 訓練後須要作的準備
     */
    @Override
    void afterTrain() {
        System.out.println("獎勵一些狗糧");
    }

    /**
     * 訓練出現異常須要作的
     *
     * @param throwable
     */
    @Override
    void exceptionTrain(Throwable throwable) {
        System.out.println("出去罰站");
    }

    /**
     * 具體訓練
     */
    @Override
    void doSomething() {
        System.out.println("握手");
    }

    /**
     * 獲取動物種類
     *
     * @return
     */
    @Override
    public int getType() {
        return 1;
    }
}

同時定義一個獅子Lion

public class Lion extends AbsTrainAnimal {
    /**
     * 訓練前須要作的準備
     */
    @Override
    void beforeTrain() {
        System.out.println("友好交流");
    }

    /**
     * 訓練後須要作的準備
     */
    @Override
    void afterTrain() {
        System.out.println("獎勵一隻雞");
    }

    /**
     * 訓練出現異常須要作的
     *
     * @param throwable
     */
    @Override
    void exceptionTrain(Throwable throwable) {
        System.out.println("緊急送往醫院");
    }

    /**
     * 具體訓練
     */
    @Override
    void doSomething() {
        System.out.println("鑽火圈");
    }

    /**
     * 獲取動物種類
     *
     * @return
     */
    @Override
    public int getType() {
        return 2;
    }
}

咱們能夠看到,Dog和Lion的動物種類是不同的,Dog爲1,Lion爲2。咱們能夠根據type區分是獅子仍是狗。但爲了不使用if else進行區分,咱們須要一個工廠類來生產這兩種動物。

@Service
public class AnimalFactory {
    
    private static List<Class<? extends IAnimal>> animalLists = Lists.newArrayList();
    private static Map<Integer, IAnimal> animalMaps = Maps.newHashMap();
    
    static {
        animalLists.add(Dog.class);
        animalLists.add(Lion.class);
    }
    
    @PostConstruct
    public void init() throws IllegalAccessException, InstantiationException {
        for (Class<? extends IAnimal> clazz : animalLists){
            Object obj = clazz.newInstance();
            animalMaps.put(obj.getType(), obj);
        }
    }

    /**
     * 構建動物類
     * @param type
     * @return
     */
    IAnimal build(int type){
        return animalMaps.get(type);
    }
}

咱們有了這個工廠類,就能夠根據不一樣的動物類型獲取不一樣的對象,並對其進行訓練。固然咱們這裏都是使用的單例模式,每一個對象只對應一個實例,若是每次都生成不一樣的實例,能夠對其進行簡單的改造便可實現。

咱們再重寫馴獸師Beast類

@Service
public class Beast {

    @Resource
    private AnimalFactory animalFactory;

    /**
     * 訓練動物,只須要知道動物的類型便可
     * @param type
     */
    public void train(int type){
        IAnimal animal = animalFactory.build(type);
        animal.train();
    }
}

能夠看到train方法只須要關係動物類型便可,不須要再根據type進行判斷動物類型在對其進行不一樣的操做。若是有新的動物加入,只須要實現AbsTrainAnimal基類,而後向AnimalFactory進行註冊便可。避免了根據不一樣type進行if else或者switch的判斷

總結

其實,上述所述的方法不但但省去了if else的判斷,也是目前比較流行的領域模型的一種實現方式。IAnimal是領域內對外暴露的惟一方式,外部領域(馴獸師)不須要關心任何內部實現的細節。內部的實現徹底集合在IAnimal內。

相關文章
相關標籤/搜索