(策略模式+工廠模式+map)套餐 Kill 項目中的switch case

接手新任務:接入第三家存證機構,看以前的代碼使用了swith case判斷使用哪家存證機構,每家存證機構的實現邏輯不同 代碼的壞味道:多層swich case。多層swich case很差維護,是時候應該重構了,java

優化前的代碼

爲了便於理解,舉個沒有業務邏輯的例子,基於這個例子上進行優化。 如今是12:47,舉個飯後吃水果的例子哈哈哈(逃 假設咱們能夠選擇的水果有香蕉、西瓜和蘋果。吃香蕉的話咱們得先剝皮,吃西瓜的話得先用水果刀切一下,若是是蘋果的話就直接吃了。將這個場景轉化爲代碼:bash

public class EatFruit {
    private static final String APPLE = "apple";
    private static final String BANANA = "banana";
    private static final String WATERMELON = "watermelon";

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        //選擇的水果種類
        String fruitType = scanner.nextLine();

        switch (fruitType) {
            case APPLE:
                eatApple();
                break;
            case BANANA:
                eatBanana();
                break;
            case WATERMELON:
                eatWatermelon();
                break;
        }
    }

    private static void eatBanana() {
        System.out.println("吃香蕉了,須要先剝下皮");
    }

    private static void eatApple() {
        System.out.println("是蘋果,能夠直接吃");
    }

    private static void eatWatermelon() {
        System.out.println("吃西瓜了,可是還得弄把水果刀切一下先");
    }
}
複製代碼

這個例子代碼量不是很大,可是實際項目中的場景確定沒有這麼簡單,多重swich case很差維護,而且萬一又加了同樣水果,還得繼續加一層case... app

趕忙優化
很容易想到策略模式(簡單的理解就是多態),水果都有吃的一個動做,可是每種水果的吃法不同

使用策略模式進行優化

Fruit.javaide

public interface Fruit {
    void eat();
}

複製代碼

Apple.java優化

public class Apple implements Fruit {
    @Override
    public void eat() {
        System.out.println("是蘋果,能夠直接吃");
    }
}
複製代碼

Banana.javaui

public class Banana implements Fruit {
    @Override
    public void eat() {
        System.out.println("吃香蕉了,須要先剝下皮");
    }
}
複製代碼

Watermelon.javaspa

public class Watermelon implements Fruit {
    @Override
    public void eat() {
        System.out.println("吃西瓜了,可是還得弄把水果刀切一下先");
    }
}
複製代碼

可是發現即便用了策略模式也難逃類型判斷 EatFruit.java3d

public class EatFruit {
    private static final String APPLE = "apple";
    private static final String BANANA = "banana";
    private static final String WATERMELON = "watermelon";

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        //選擇的水果種類
        String fruitType = scanner.nextLine();
        Fruit fruit = null;

        switch (fruitType) {
            case APPLE:
                fruit = new Apple();
                break;
            case BANANA:
                fruit = new Banana();
                break;
            case WATERMELON:
                fruit = new Watermelon();
                break;
        }

        fruit.eat();
    }
}
複製代碼

使用策略模式具備良好的擴展性,兵來將擋,再給我添加十種水果都不怕,水果種類太多會使水果的實現類暴增。如今來一種水果無需在原先的業務類(EatFruit)裏修改不少的代碼邏輯,只要實現接口並加個條件判斷就行了。可是使用策略模式時,咱們須要知道具體的實現類,具體的實現類須要對外暴露code

使用工廠模式

將類型判斷放到工廠類中 工廠模式:在建立對象時不會對客戶端暴露建立邏輯,而且是經過使用一個共同的接口來指向新建立的對象,這裏共同的接口就是Fruit。粗暴的講,工廠模式就是把建立同一類型對象邏輯寫在了一個方法裏 FruitFactory .javacdn

public class FruitFactory {
    public static Fruit getFruit(String fruitType) {
        if ("apple".equals(fruitType)) {
            return new Apple();
        }

        if ("banana".equals(fruitType)) {
            return new Banana();
        }

        return new Watermelon();
    }
}
複製代碼

EatFruit .java

public class EatFruit {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        //選擇的水果種類
        String fruitType = scanner.nextLine();
        Fruit fruit = FruitFactory.getFruit(fruitType);
        fruit.eat();
    }
}
複製代碼

到如今爲止EatFruit中的業務代碼已經很清晰了。 使用工廠模式具備良好的封裝性,這下媽媽不再用關係建立類的過程,甚至連建立的實際類的都無需關心,實現瞭解耦,實際類的修改變化都不會影響上層業務
可是工廠類中仍是有不少if的,革命還沒有成功,仍須要進行優化。

map奧特曼來了

FruitFactory.java

public class FruitFactory {
    private static Map<String, Fruit> fruitMap = new HashMap<>();

    static {
        fruitMap.put("apple", new Apple());
        fruitMap.put("banana", new Banana());
        fruitMap.put("watermelon", new Watermelon());
    }

    public static Fruit getFruit(String fruitType) {
       return fruitMap.get(fruitType);
    }
}
複製代碼

經過使用map,fruitType<T extend Fruit>進行一一映射,和條件判斷說byebye~,代碼也很優雅 最終項目結構類圖:

類圖.png

========================如下內容更新於2018.11.12======================

有網友評論能夠用反射替代map,若是傳進來的是Class類型的值就能夠用反射。用反射的好處是在添加多種水果,工廠類的代碼都無需進行變更 經過反射替代map: FruitFactory2.java

public class FruitFactory2 {
    public static <T extends Fruit> T getFruit(Class<T> fruitClass) throws IllegalAccessException, InstantiationException {
        return fruitClass.newInstance();
    }
}
複製代碼
相關文章
相關標籤/搜索