消除程式碼中的 if-else/switch-case
在很多時候,我們程式碼中會有很多分支,而且分支下面的程式碼又有一些複雜的邏輯,相信很多人都喜歡用 if-else/switch-case 去實現。做的不好的會直接把實現的程式碼放在 if-else/switch-case 的分支之下:
switch ( type ) { case case1: ... ... break; case case2: ... ... break; case case3: ... ... break default: return null; } 複製程式碼
這樣的程式碼不僅冗長,讀起來也非常困難。做的好一點的會把這些邏輯封裝成函式然後在分支中呼叫:
switch ( type ) { case case1: return case1Func(); case case2: return case2Func(); case case3: return case3Func(); default: return null; } 複製程式碼
即使這樣也是面向過程思維的寫法,以前寫 C 程式的時候也總喜歡這樣寫,毫無設計模式可言。不僅違背開閉原則,而且隨著 switch-case 分支的增多,該段程式碼只會越來越冗長。其實這種程式碼已經有成熟的模式去消除諸多的 if-else/switch-case 分支。本文就教大家在 Spring 中如何用註解+策略模式+簡單工廠的方式消除 if-else/switch-case 。我們就拿 QQ 空間的個人中心舉例子,假如 QQ 空間個人中心有四個 tab 分別是列出我的說說、我的日誌、我的照片和我的訪客。一般的後臺程式碼很有可能如下:
//各個 tab 名稱的列舉: public enum UserRelatedType { /** * 說說 */ SHUOSHUO("說說"), /** * 日誌 */ RIZHI("日誌"), /** * 釋出 */ ZHAOPIAN("照片"), /** * 訪客 */ FANGKE(""); private String desc; UserRelatedType(String desc) { this.desc = desc; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; } } 複製程式碼
列出 QQ 使用者個人中心相關 tab 的程式碼:
public List<UserRelatedVO> listRelated(UserRelatedQuery query){ UserRelatedType relatedType = UserRelatedType.valueOf(StringUtils.upperCase(query.getType()) ); switch ( relatedType ) { case SHUOSHUO: return listRelatedShuoshuo( query ); case RIZHI: return listRelatedRizhi( query ); case ZHAOPIAN: return listRelatedZhaopian( query ); case FANGKE: return listRelatedFangke( query ); default: return null; } } 複製程式碼
而採用註解+策略模式+簡單工廠,重構後代碼如下:
1、先定義了個介面,所有 tab 都要實現該介面。其中 list 是 tab 資料展示的方法。
public interface UserRelated { /** * 列出詳細資訊 * * @param query * @return */ List<UserRelatedVO> list(UserRelatedQuery query); } 複製程式碼
2、定義具體的各個 tab 的實現,繼承 UserRelated 策略介面
- 我的說說
@Component("userRelatedShuoshuo") @RelatedTypeAnnotation( value = UserRelatedType.SHUOSHUO ) public class UserRelatedShuoshuo implements UserRelated { @Override public List<UserRelatedVO> list(UserRelatedQuery query) { System.out.println("我的說說!"); return list; } } 複製程式碼
- 我的日誌
@Component("userRelatedRizhi") @RelatedTypeAnnotation( value = UserRelatedType.RIZHI ) public class UserRelatedRizhi implements UserRelated { @Override public List<UserRelatedVO> list(UserRelatedQuery query) { System.out.println("我的日誌!"); return list; } } 複製程式碼
- 我的照片
@Component("userRelatedZhaopian") @RelatedTypeAnnotation( value = UserRelatedType.ZHAOPIAN ) public class UserRelatedZhaopian implements UserRelated { @Override public List<UserRelatedVO> list(UserRelatedQuery query) { System.out.println("我的照片!"); return list; } } 複製程式碼
- 我的訪客
@Component("userRelatedFangke") @RelatedTypeAnnotation( value = UserRelatedType.FANGKE ) public class UserRelatedFangke implements UserRelated { @Override public List<UserRelatedVO> list(UserRelatedQuery query) { System.out.println("我的訪客!"); return list; } } 複製程式碼
- 定義註解,用來識別身份。
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) public @interface RelatedTypeAnnotation { /** * 使用者相關型別名稱 */ UserRelatedType value(); } 複製程式碼
- 定義一個簡單工廠,用來生產各種 tab 物件。
@Component public class UserRelatedFactory { private static Map<UserRelatedType, UserRelated> userRelatedMap = Maps.newConcurrentMap(); //工廠將 Spring 裝配的相關的 Bean 用 Map 儲存起來 public UserRelatedFactory(){ Map<String, Object> beanMap = SpringContextUtil.getContext().getBeansWithAnnotation(RelatedTypeAnnotation.class); for(Object userRelated : beanMap.values()) { RelatedTypeAnnotation annotation = userRelated.getClass().getAnnotation(RelatedTypeAnnotation.class); userRelatedMap.put(annotation.value(), (UserRelated)userRelated); } } public static UserRelated createRelated(UserRelatedType relatedType) { return userRelatedMap.get( relatedType ); } } 複製程式碼
- 呼叫的程式碼(listRelated 會在 controller 中被呼叫)。
public List<UserRelatedVO> listRelated(UserRelatedQuery query){ UserRelatedType relatedType = UserRelatedType.valueOf(StringUtils.upperCase(query.getType()) ); UserRelated related = UserRelatedFactory.createRelated( relatedType ); if( related != null ) { return related.list( query ); } else { return null; } } 複製程式碼
重構後的程式碼如果需要再新增一種 tab,比如我的好友,只需要新增一種型別繼承 UserRelated 實現其中的 list,並加上相應的註解即可。
其實這是一種通用的解決方案,當你 if-else/switch-case 的分支超過 3 個、且分支程式碼相似且冗長的情況下就應該考慮這種模式。這種模式寫出的程式碼面向物件、清晰、易擴充套件還高大上,何樂而不為呀,趕緊試試吧!