接手新任務:接入第三家存證機構,看之前的代碼使用了swith case判斷使用哪家存證機構,每家存證機構的實現邏輯不一樣
代碼的壞味道:多層swich case。多層swich case不好維護,是時候應該重構了,
優化前的代碼
爲了便於理解,舉個沒有業務邏輯的例子,基於這個例子上進行優化。 現在是12:47,舉個飯後喫水果的例子哈哈哈(逃
假設我們可以選擇的水果有香蕉、西瓜和蘋果。喫香蕉的話我們得先剝皮,喫西瓜的話得先用水果刀切一下,如果是蘋果的話就直接吃了。將這個場景轉化爲代碼:
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...
很容易想到策略模式(簡單的理解就是多態),水果都有喫的一個動作,但是每種水果的喫法不一樣
使用策略模式進行優化
Fruit.java
public interface Fruit {
void eat();
}
Apple.java
public class Apple implements Fruit {
@Override
public void eat() {
System.out.println("是蘋果,可以直接喫");
}
}
Banana.java
public class Banana implements Fruit {
@Override
public void eat() {
System.out.println("喫香蕉了,需要先剝下皮");
}
}
Watermelon.java
public class Watermelon implements Fruit {
@Override
public void eat() {
System.out.println("喫西瓜了,但是還得弄把水果刀切一下先");
}
}
但是發現即使用了策略模式也難逃類型判斷 EatFruit.java
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)裏修改很多的代碼邏輯,只要實現接口並加個條件判斷就好了。但是使用策略模式時,我們需要知道具體的實現類,具體的實現類需要對外暴露
使用工廠模式
將類型判斷放到工廠類中 工廠模式:在創建對象時不會對客戶端暴露創建邏輯,並且是通過使用一個共同的接口來指向新創建的對象,這裏共同的接口就是Fruit。粗暴的講,工廠模式就是把創建同一類型對象邏輯寫在了一個方法裏 FruitFactory .java
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~,代碼也很優雅 最終項目結構類圖:
有網友評論可以用反射替代map,如果傳進來的是Class類型的值就可以用反射。用反射的好處是在添加多種水果,工廠類的代碼都無需進行變動
通過反射替代map: FruitFactory2.java
public class FruitFactory2 {
public static <T extends Fruit> T getFruit(Class<T> fruitClass) throws IllegalAccessException, InstantiationException {
return fruitClass.newInstance();
}
}