通常來講,隨着咱們項目的迭代以及業務的愈來愈複雜,項目中的分支判斷會原來越多。當項目中涉及到複雜的業務判斷或者分支邏輯時,咱們就須要考慮是否須要對項目進行重構了,或者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對象
在作優化以前,咱們須要先弄清楚咱們的目的。咱們是馬戲團的馴獸師,目的是訓練動物。blog
有了上面的目的以後,咱們就能夠定義一個模型animal接口
public interface IAnimal { /** * 獲取動物種類 * @return */ int getType(); /** * 訓練動做 */ void train(); }
Animal只暴露兩個方法,其中doSomething是專門用來訓練動物的,至於如何訓練全都由子類實現。get
而後咱們定義一個子類實現這個接口,用來具體化如何訓練動物
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()進行了各類功能的細化
由於全部動物的以上四個方法可能都不相同,因此咱們聲明爲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內。