先來一張鎮樓圖感覺一下 if else 的魔法吧。java
有個場景,50張字典表,須要爲其餘服務提供一個統一的接口來校驗用戶輸入的字典表 id 是否合法。編程
校驗邏輯已經很清晰了,根據參數選擇對應的表校驗 id 是否存在。app
if("table_a".equals(table)) { // check id } if("table_b".equals(table)) { // check id } if("table_c".equals(table)) { // check id }...
再加上參數校驗,函數調用,@Autowired bean 等等,一坨幾百行的代碼 ok 了。再新加表再加 if else 就好了,😋 完美。ide
如此,N 年後另外一個可憐的小夥伴就看到這坨東西。函數
回想上面的場景,實際上就是要根據表名去肯定 id 是否存在表中,那麼只要將表名與操做對應起來就好了。故而採用哈希表的形式,將表名與操做對應起來。部分代碼以下:優化
// 用於保存表與 Function 的對應關係 private final Map<String, Function<Object, Object>> actionMappings = new ConcurrentHashMap<>(50); @PostConstruct private void init() { // map 初始化 actionMappings.put(TableConstants.TABLE_A, (params) -> tableAManager.getById(params)); } /** * 校驗邏輯 * *@param table *@param id */ public boolean valid(String table, Long id) { Object object = actionMappings.get(table).apply(id); // 不存在則校驗失敗 return !Objects.isNull(object); }
如此,N 多行 if 被消除了,這種編程方式也叫作表驅動。雖然 if 沒有了,可是在初始化 actionMappings 的時候仍是不少行重複代碼。下面採用註解方式解決:this
/** * 標記此註解的 bean 會加入基礎數據校驗全局 Function Map * * @author aysaml * @date 2020/5/7 */ @Documented @Inherited @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface ValidHandler { TABLE_ENUM value(); }
value 是表名枚舉,在須要的類上面加上此註解便可。同時定義一個 context 用來專門存儲 actionMappings 。spa
/** * 數據校驗上下文對象,用於保存各表的 Function Map * * @author aysaml * @date 2020/5/7 */ @Component public class CommonDataValidContext { private static final Logger LOGGER = LoggerFactory.getLogger(CommonDataValidContext.class); private final Map<String, Function<Object, Object>> actionMappings = new ConcurrentHashMap<>(50); /** * 方法加入 mappings * * @param model 表名 * @param action 方法 */ public void putAction(String model, Function<Object, Object> action) { if (!Objects.isNull(action)) { actionMappings.put(model, action); LOGGER.info( "[{}] add to CommonDataValidContext actionMappings, actionMappings size : {}", model, actionMappings.size()); } } /** * 執行方法獲取返回結果 * * @param model * @param param * @return */ public <P, R> R apply(String model, P param) { if (actionMappings.containsKey(model)) { return (R) actionMappings.get(model).apply(param); } else { LOGGER.error("執行數據校驗時model={}不存在!", model); throw new RuntimeException("基礎數據校驗時發生錯誤:" + model + "表不存在!"); } } /** * 判斷 mappings 中是否含有給定 model 的處理方法 * * @param model * @return */ public boolean containsKey(String model) { return actionMappings.containsKey(model); } /** * 校驗執行方法的返回值是否爲空 * * @param model * @param param * @param <P> * @return */ public <P> boolean validResultIsNull(String model, P param) { return Objects.isNull(this.apply(model, param)); } }
而後經過監聽器的方式,將含有 ValidHandler 註解的方法加入 actionMappings 。code
/** * 基礎數據校驗處理方法監聽器 * * @author aysaml * @date 2020/5/7 */ @Component public class CommonValidActionListener implements ApplicationListener<ContextRefreshedEvent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { Map<String, Object> beans = event.getApplicationContext().getBeansWithAnnotation(ValidHandler.class); CommonDataValidContext commonDataValidContext = event.getApplicationContext().getBean(CommonDataValidContext.class); beans.forEach( (name, bean) -> { ValidHandler validHandler = bean.getClass().getAnnotation(ValidHandler.class); commonDataValidContext.putAction( validHandler.value().code(), (param) -> { try { return bean.getClass().getMethod("getById", Long.class).invoke(bean, param); } catch (Exception e) { e.printStackTrace(); } return null; }); }); } }
這樣可使代碼在邏輯表達上會更清晰,以下:對象
if (condition) { // do something } else { return xxx; }
按照逆向思惟來,優化以下:
if (!condition) { return xxx; } // do something
還有一種常見的傻瓜編程(若有冒犯,敬請見諒,對碼不對人🙏 ):
if(a > 0) { return true; } else { return false; }
話很少說了,直接 return a > 0;
不香嗎?
簡單來講就是根據不一樣的參數執行不一樣的業務邏輯。
以下:
if (status == 0) { // 業務邏輯處理 0 } else if (status == 1) { // 業務邏輯處理 1 } else if (status == 2) { // 業務邏輯處理 2 } else if (status == 3) { // 業務邏輯處理 3 }...
優化以下:
interface A { void run() throws Exception; } class A0 implements A { @Override void run() throws Exception { // 業務邏輯處理 0 } } class A1 implements A { @Override void run() throws Exception { // 業務邏輯處理 1 } } // ...
而後策略對象存放在一個 Map 中,以下:
A a = map.get(param); a.run();
public enum Status { NEW(0) { @Override void run() { //do something } }, RUNNABLE(1) { @Override void run() { //do something } }; public int statusCode; abstract void run(); Status(int statusCode){ this.statusCode = statusCode; } }
從新定義策略枚舉
public enum Aenum { A_0 { @Override void run() { //do something } }, A_1 { @Override void run() { //do something } }; //... abstract void run(); }
經過枚舉優化以後的代碼以下
Aenum a = Aenum.valueOf(param); a.run();
Optional主要用於非空判斷,是 Java 8 提供的新特性。
使用以前:
if (user == null) { //do action 1 } else { //do action2 }
若是登陸用戶爲空,執行action1,不然執行action 2,使用Optional優化以後,讓非空校驗更加優雅,間接的減小if操做
Optional<User> userOptional = Optional.ofNullable(user); userOptional.map(action1).orElse(action2);
就是上面的表驅動編程方法。
歡迎訪問 我的博客 獲取更多知識分享。