工做中常常碰到不少分支的處理邏輯,可是每一個分支的處理邏輯類似,只是具體某些字段或者說比較邏輯等有不一樣。若是不思考直接寫代碼很容易出現一大堆的if else出現,這種代碼難於維護、冗餘特別嚴重。這個時候其實咱們須要對於結構進行從新設計,提升擴展性和可維護性。spring
拿個實際的例子,最近在弄的某促銷下系統,對於一個展現狀況,在活動類型=1的時候要xxx,活動類型=2要xxx,這個若是用ifelse很容易實現,可是裏面很容易出現特別多的冗餘代碼,拆出來很差拆,不拆又特別糟心。好比下面的代碼:緩存
//1.普通 ifelse方式 public static Object handleByIfElse(int id) { if (id == 1) { return "aaa"; } if (id == 2) { return 2; } return null; }
咱們實際狀況中確定比這個要複雜的多,return通常也會有大篇幅的代碼段存在,代碼的可讀性很是差。數據結構
因而咱們能夠設計一下處理邏輯,在肯定每一個分支下面的處理邏輯大致相同後,咱們能夠建立一個抽象類,抽象類中有一個抽象方法供子類覆蓋,同時還有一個protected的方法是全部子類公用的,這些protected的方法就是ifelse分支中重複的代碼。如此,結構變成:框架
public abstract class Handler { abstract Object handle(); protected HandleConfigurations getConfiguration() { return new HandleConfigurations(); } // @PostConstruct protected void regist() { HandlerEntrance.regist(this); } }
代碼很是簡單,拿個postconstruct的內容後面解釋,其他的部分其實就是一個protected方法供全部子類共享,一個abstract方法供全部子類覆蓋。jvm
public class Handler1 extends Handler { @Override Object handle() { HandleConfigurations handleConfigurations = getConfiguration(); return handleConfigurations.toString(); } }
public class Handler2 extends Handler { @Override Object handle() { return null; } }
如此,咱們把全部的分支所有重構稱爲一個一個的子類,須要變更哪個直接找到對應的子類進行修改。有新的分支,就能夠直接新增一個子類,這樣子代碼可讀性提升了不少。ide
可是咱們還有一件須要作的事情就是須要讓請求具體分發到具體的子類上面,咱們能夠簡單採用switch來進行:post
//2.經過switch方式 public static Object handleBySwitch(int id) { switch (id) { case 1: return new Handler1().handle(); case 2: return new Handler2().handle(); default: return null; } }
咱們會發現,若是咱們這樣子作,switch會限制只有基本類型和枚舉才能夠,若是其餘數據結構咱們還須要用ifelse,並且代碼也至關不優雅。因此咱們採用map來進行:this
//3.經過初始化好的map進行映射 private static Map<Integer, Handler> handlerMap = Maps.newHashMap(); static { handlerMap.put(1, new Handler1()); handlerMap.put(2, new Handler2()); } public static Object handleByMap(int id) { return handlerMap.get(id).handle(); }
咱們初始化好,傳入參數與具體處理的子類的對應關係,在入口拿到參數後得到子類的實例後直接進行處理。可是這樣子,咱們不得不維護一個map來作對應關係,或者在xml裏面進行配置,這樣子若是map中的值特別多,也會變成一大坨,至關難看。spa
因此咱們須要想一個辦法來避免這種狀況,反射能夠作到咱們想要的:設計
//4.經過反射 public static Object handleByReflection(int id) { try { Handler handler = (Handler) Class.forName("com.mt.design1.Handler" + id).newInstance(); return handler.handle(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; }
經過反射,咱們能夠直接根據傳入的參數直接生成到具體的類名字,而後拿到這個類的實例。可是這個作法還有一個比較噁心的地方就是咱們須要寫死類的絕對路徑,若是咱們這麼作,代碼發生重構,會出現classnotfound的狀況,並且這種傳入辦法效率並不高,固然咱們能夠在初始化後放入map中,而後下次衝map的緩存中得到類的實例就能夠。然而這麼作仍是以爲不太舒服。
咱們回到第三步的內容,若是咱們能夠作到map由子類自動去註冊,這樣子咱們彷佛就能夠在傳入的時候不作考慮,也不用專門維護對應關係,有新邏輯只要專一於把具體處理的子類寫完就能夠了。因而咱們能夠這樣子作:
一、定義一個註解,用於表示該子類的key屬性:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface IdAnno { int id(); }
咱們將該註解注入到具體的子類中:
@IdAnno(id = 1) public class Handler1 extends Handler { @Override Object handle() { HandleConfigurations handleConfigurations = getConfiguration(); return handleConfigurations.toString(); } }
這種作法可讓咱們在一個子類中得到了全部須要的內容,下一步咱們要考慮的就是將這個實例在系統啓動的階段就加載到jvm中:
//5.經過註解初始化和刷新map private static Map<Integer, Handler> handlerMapByAnnotation = Maps.newHashMap(); public static void regist(Handler handler) { if (handler.getClass().isAnnotationPresent(IdAnno.class)) { handlerMapByAnnotation.put(handler.getClass().getAnnotation(IdAnno.class).id(), handler); } }
剛纔抽象類中的@PostConstrct註解的方法咱們就瞭解到,若是子類在初始化的時候,就會進行註冊操做,註冊時候會將類的實例放入緩存的map中(其實和spring的beanfactory相似),用的時候直接得到就能夠了:
//5.經過註解初始化和刷新map private static Map<Integer, Handler> handlerMapByAnnotation = Maps.newHashMap(); public static void regist(Handler handler) { if (handler.getClass().isAnnotationPresent(IdAnno.class)) { handlerMapByAnnotation.put(handler.getClass().getAnnotation(IdAnno.class).id(), handler); } } public static Object handleByAnnotation(int id) { return handlerMapByAnnotation.get(id).handle(); }
最後,咱們考慮的就是在系統的啓動階段就得把子類的初始化的動做給完成,若是你使用了spring框架,那能夠在子類上面加上@Service @Component等註解就能夠:
@Service @IdAnno(id = 1) public class Handler1 extends Handler { @Override Object handle() { HandleConfigurations handleConfigurations = getConfiguration(); return handleConfigurations.toString(); } }
若是並無使用spring框架,那這個裏面能夠本身寫一個讀取某文件夾下面的類而後進行初始化的util執行,可是這種作法就得不償失,仍是能夠用3或者4來進行了。
Ok,探索到此爲止。