做者:小傅哥
博客:https://bugstack.cn - 原創系列專題文章
html
沉澱、分享、成長,讓本身和他人都能有所收穫!😄
寫好代碼三個關鍵點
java
若是把寫代碼想象成家裏的軟裝,你確定會想到家裏須要有一個很是不錯格局最好是南北通透的,買回來的傢俱最好是品牌保證質量的,以後呢是大小合適,不能擺放完了看着彆扭。那麼把這一過程抽象成寫代碼就是須要三個核心的關鍵點;架構
(房間的格局)、命名
(品牌和質量)、註釋
(尺寸大小說明書),只有這三個點都作好才能完成出一套賞心悅目的家。程序員
平原走碼🐎易放難收
數據庫
上學期間你寫了多少代碼?上班一年你能寫多少代碼?回家本身學習寫了多少代碼?我的素養的技術棧地基都是一塊一塊磚碼出來的,寫的越廣越深,根基就越牢固。當根基牢固了之後在再上層建設就變得迎刃而解了,也更容易建設了。每每最難的就是一層一層階段的突破,突破就像破殼同樣,也像夯實地基,短期看不到成績,也看不出高度。但之後誰能走的穩,就靠着默默的沉澱。編程
技術傳承的重要性
設計模式
多是如今時間節奏太快,一個需求下來巴不得當天就上線(這個需求很簡單,怎麼實現我無論,明天上線!
),致使團隊的人都很慌
、很急
、很累
、很崩潰
,最終反反覆覆的人員更替,項目在這個過程當中也交接了N次,文檔不全、代碼混亂、錯綜複雜,誰在後面接手也都只能修修補補,就像爛尾樓。這個沒有傳承、沒有沉澱的項目,很難跟隨業務的發展。最終!根基不牢,一地雞毛。安全
bugstack蟲洞棧
,回覆源碼下載
獲取(打開獲取的連接,找到序號18)工程 | 描述 |
---|---|
itstack-demo-design-19-00 | 場景模擬工程;模擬營銷活動操做服務(查詢、審覈) |
itstack-demo-design-19-01 | 使用一坨代碼實現業務需求 |
itstack-demo-design-19-02 | 經過設計模式優化改造代碼,產生對比性從而學習 |
狀態模式描述的是一個行爲下的多種狀態變動,好比咱們最多見的一個網站的頁面,在你登陸與不登陸下展現的內容是略有差別的(不登陸不能展現我的信息
),而這種登陸
與不登陸
就是咱們經過改變狀態,而讓整個行爲發生了變化。微信
至少80後、90後的小夥伴基本都用過這種磁帶放音機(可能沒有這個好看
),它的上面是一排按鈕,當放入磁帶後,經過上面的按鈕就可讓放音機播放磁帶上的內容(listen to 英語聽力考試
),並且有些按鈕是互斥的,當在某個狀態下才能夠按另外的按鈕(這在設計模式裏也是一個關鍵的點
)。數據結構
在本案例中咱們模擬營銷活動審覈狀態流轉場景(一個活動的上線是多個層級審覈上線的)架構
在上圖中也能夠看到咱們的流程節點中包括了各個狀態到下一個狀態扭轉的關聯條件,好比;審覈經過才能到活動中,而不能從編輯中直接到活動中,而這些狀態的轉變就是咱們要完成的場景處理。
大部分程序員基本都開發過相似的業務場景,須要對活動或者一些配置須要審覈後才能對外發布,而這個審覈的過程每每會隨着系統的重要程度而設立多級控制,來保證一個活動能夠安全上線,避免形成資損。
固然有時候會用到一些審批流的過程配置,也是很是方便開發相似的流程的,也能夠在配置中設定某個節點的審批人員。但這不是咱們主要體現的點,在本案例中咱們主要是模擬學習對一個活動的多個狀態節點的審覈控制。
itstack-demo-design-19-00 └── src └── main └── java └── org.itstack.demo.design ├── ActivityInfo.java ├── Status.java └── ActivityService.java
Status
)、活動對象(ActivityInfo
)、活動服務(ActivityService
),三個服務類。public class ActivityInfo { private String activityId; // 活動ID private String activityName; // 活動名稱 private Enum<Status> status; // 活動狀態 private Date beginTime; // 開始時間 private Date endTime; // 結束時間 // ...get/set }
public enum Status { // 1建立編輯、2待審覈、3審覈經過(任務掃描成活動中)、4審覈拒絕(能夠撤審到編輯狀態)、5活動中、6活動關閉、7活動開啓(任務掃描成活動中) Editing, Check, Pass, Refuse, Doing, Close, Open }
public class ActivityService { private static Map<String, Enum<Status>> statusMap = new ConcurrentHashMap<String, Enum<Status>>(); public static void init(String activityId, Enum<Status> status) { // 模擬查詢活動信息 ActivityInfo activityInfo = new ActivityInfo(); activityInfo.setActivityId(activityId); activityInfo.setActivityName("早起學習打卡領獎活動"); activityInfo.setStatus(status); activityInfo.setBeginTime(new Date()); activityInfo.setEndTime(new Date()); statusMap.put(activityId, status); } /** * 查詢活動信息 * * @param activityId 活動ID * @return 查詢結果 */ public static ActivityInfo queryActivityInfo(String activityId) { // 模擬查詢活動信息 ActivityInfo activityInfo = new ActivityInfo(); activityInfo.setActivityId(activityId); activityInfo.setActivityName("早起學習打卡領獎活動"); activityInfo.setStatus(statusMap.get(activityId)); activityInfo.setBeginTime(new Date()); activityInfo.setEndTime(new Date()); return activityInfo; } /** * 查詢活動狀態 * * @param activityId 活動ID * @return 查詢結果 */ public static Enum<Status> queryActivityStatus(String activityId) { return statusMap.get(activityId); } /** * 執行狀態變動 * * @param activityId 活動ID * @param beforeStatus 變動前狀態 * @param afterStatus 變動後狀態 b */ public static synchronized void execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) { if (!beforeStatus.equals(statusMap.get(activityId))) return; statusMap.put(activityId, afterStatus); } }
queryActivityInfo
、queryActivityStatus
、execStatus
。數據庫
或者Redis
中獲取。這裏咱們先使用最粗暴的方式來實現功能
對於這樣各類狀態的變動,最讓咱們直接想到的就是使用if
和else
進行判斷處理。每個狀態能夠流轉到下一個什麼狀態,均可以使用嵌套的if
實現。
itstack-demo-design-19-01 └── src └── main └── java └── org.itstack.demo.design ├── ActivityExecStatusController.java └── Result.java
ActivityExecStatusController
、Result
,一個是處理流程狀態,另一個是返回的對象。public class ActivityExecStatusController { /** * 活動狀態變動 * 1. 編輯中 -> 提審、關閉 * 2. 審覈經過 -> 拒絕、關閉、活動中 * 3. 審覈拒絕 -> 撤審、關閉 * 4. 活動中 -> 關閉 * 5. 活動關閉 -> 開啓 * 6. 活動開啓 -> 關閉 * * @param activityId 活動ID * @param beforeStatus 變動前狀態 * @param afterStatus 變動後狀態 * @return 返回結果 */ public Result execStatus(String activityId, Enum<Status> beforeStatus, Enum<Status> afterStatus) { // 1. 編輯中 -> 提審、關閉 if (Status.Editing.equals(beforeStatus)) { if (Status.Check.equals(afterStatus) || Status.Close.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "變動狀態成功"); } else { return new Result("0001", "變動狀態拒絕"); } } // 2. 審覈經過 -> 拒絕、關閉、活動中 if (Status.Pass.equals(beforeStatus)) { if (Status.Refuse.equals(afterStatus) || Status.Doing.equals(afterStatus) || Status.Close.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "變動狀態成功"); } else { return new Result("0001", "變動狀態拒絕"); } } // 3. 審覈拒絕 -> 撤審、關閉 if (Status.Refuse.equals(beforeStatus)) { if (Status.Editing.equals(afterStatus) || Status.Close.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "變動狀態成功"); } else { return new Result("0001", "變動狀態拒絕"); } } // 4. 活動中 -> 關閉 if (Status.Doing.equals(beforeStatus)) { if (Status.Close.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "變動狀態成功"); } else { return new Result("0001", "變動狀態拒絕"); } } // 5. 活動關閉 -> 開啓 if (Status.Close.equals(beforeStatus)) { if (Status.Open.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "變動狀態成功"); } else { return new Result("0001", "變動狀態拒絕"); } } // 6. 活動開啓 -> 關閉 if (Status.Open.equals(beforeStatus)) { if (Status.Close.equals(afterStatus)) { ActivityService.execStatus(activityId, beforeStatus, afterStatus); return new Result("0000", "變動狀態成功"); } else { return new Result("0001", "變動狀態拒絕"); } } return new Result("0001", "非可處理的活動狀態變動"); } }
ifelse
,基本這也是大部分初級程序員的開發方式。但基本不可能不迭代
)。並且隨着狀態和需求變化,會愈來愈難以維護,後面的人也很差看懂而且很容易填充其餘的流程進去。愈來愈亂就是從點滴開始的
@Test public void test() { // 初始化數據 String activityId = "100001"; ActivityService.init(activityId, Status.Editing); ActivityExecStatusController activityExecStatusController = new ActivityExecStatusController(); Result resultRefuse = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Refuse); logger.info("測試結果(編輯中To審覈拒絕):{}", JSON.toJSONString(resultRefuse)); Result resultCheck = activityExecStatusController.execStatus(activityId, Status.Editing, Status.Check); logger.info("測試結果(編輯中To提交審覈):{}", JSON.toJSONString(resultCheck)); }
編輯中
到審覈拒絕
,另一個是從編輯中到提交審覈
。審覈拒絕
的,這中間還須要提審
。23:24:30.774 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果(編輯中To審覈拒絕):{"code":"0001","info":"變動狀態拒絕"} 23:24:30.778 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果(編輯中To提交審覈):{"code":"0000","info":"變動狀態成功"} Process finished with exit code 0
接下來使用狀態模式來進行代碼優化,也算是一次很小的重構。
重構的重點每每是處理掉ifelse
,而想處理掉ifelse
基本離不開接口與抽象類,另外還須要從新改造代碼結構。
itstack-demo-design-19-02 └── src └── main └── java └── org.itstack.demo.design ├── event │ ├── CheckState.java │ └── CloseState.java │ └── DoingState.java │ └── EditingState.java │ └── OpenState.java │ └── PassState.java │ └── RefuseState.java ├── Result.java ├── State.java └── StateHandler.java
狀態模式模型結構
提審、審覈、拒審等
)。if
語言進行判斷了。StateHandler
對狀態流程的統一處理,裏面提供Map
結構的各項服務接口調用,也就避免了使用if
判斷各項狀態轉變的流程。public abstract class State { /** * 活動提審 * * @param activityId 活動ID * @param currentStatus 當前狀態 * @return 執行結果 */ public abstract Result arraignment(String activityId, Enum<Status> currentStatus); /** * 審覈經過 * * @param activityId 活動ID * @param currentStatus 當前狀態 * @return 執行結果 */ public abstract Result checkPass(String activityId, Enum<Status> currentStatus); /** * 審覈拒絕 * * @param activityId 活動ID * @param currentStatus 當前狀態 * @return 執行結果 */ public abstract Result checkRefuse(String activityId, Enum<Status> currentStatus); /** * 撤審撤銷 * * @param activityId 活動ID * @param currentStatus 當前狀態 * @return 執行結果 */ public abstract Result checkRevoke(String activityId, Enum<Status> currentStatus); /** * 活動關閉 * * @param activityId 活動ID * @param currentStatus 當前狀態 * @return 執行結果 */ public abstract Result close(String activityId, Enum<Status> currentStatus); /** * 活動開啓 * * @param activityId 活動ID * @param currentStatus 當前狀態 * @return 執行結果 */ public abstract Result open(String activityId, Enum<Status> currentStatus); /** * 活動執行 * * @param activityId 活動ID * @param currentStatus 當前狀態 * @return 執行結果 */ public abstract Result doing(String activityId, Enum<Status> currentStatus); }
活動ID
)、currentStatus(當前狀態
),只有他們的具體實現是不一樣的。編輯
public class EditingState extends State { public Result arraignment(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Check); return new Result("0000", "活動提審成功"); } public Result checkPass(String activityId, Enum<Status> currentStatus) { return new Result("0001", "編輯中不可審覈經過"); } public Result checkRefuse(String activityId, Enum<Status> currentStatus) { return new Result("0001", "編輯中不可審覈拒絕"); } @Override public Result checkRevoke(String activityId, Enum<Status> currentStatus) { return new Result("0001", "編輯中不可撤銷審覈"); } public Result close(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Close); return new Result("0000", "活動關閉成功"); } public Result open(String activityId, Enum<Status> currentStatus) { return new Result("0001", "非關閉活動不可開啓"); } public Result doing(String activityId, Enum<Status> currentStatus) { return new Result("0001", "編輯中活動不可執行活動中變動"); } }
提審
public class CheckState extends State { public Result arraignment(String activityId, Enum<Status> currentStatus) { return new Result("0001", "待審覈狀態不可重複提審"); } public Result checkPass(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Pass); return new Result("0000", "活動審覈經過完成"); } public Result checkRefuse(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Refuse); return new Result("0000", "活動審覈拒絕完成"); } @Override public Result checkRevoke(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Editing); return new Result("0000", "活動審覈撤銷回到編輯中"); } public Result close(String activityId, Enum<Status> currentStatus) { ActivityService.execStatus(activityId, currentStatus, Status.Close); return new Result("0000", "活動審覈關閉完成"); } public Result open(String activityId, Enum<Status> currentStatus) { return new Result("0001", "非關閉活動不可開啓"); } public Result doing(String activityId, Enum<Status> currentStatus) { return new Result("0001", "待審覈活動不可執行活動中變動"); } }
checkRefuse
這個方法對於不一樣的類中有不一樣的實現,也就是不一樣狀態下能作的下一步流轉操做已經能夠在每個方法中具體控制了。public class StateHandler { private Map<Enum<Status>, State> stateMap = new ConcurrentHashMap<Enum<Status>, State>(); public StateHandler() { stateMap.put(Status.Check, new CheckState()); // 待審覈 stateMap.put(Status.Close, new CloseState()); // 已關閉 stateMap.put(Status.Doing, new DoingState()); // 活動中 stateMap.put(Status.Editing, new EditingState()); // 編輯中 stateMap.put(Status.Open, new OpenState()); // 已開啓 stateMap.put(Status.Pass, new PassState()); // 審覈經過 stateMap.put(Status.Refuse, new RefuseState()); // 審覈拒絕 } public Result arraignment(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).arraignment(activityId, currentStatus); } public Result checkPass(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).checkPass(activityId, currentStatus); } public Result checkRefuse(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).checkRefuse(activityId, currentStatus); } public Result checkRevoke(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).checkRevoke(activityId, currentStatus); } public Result close(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).close(activityId, currentStatus); } public Result open(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).open(activityId, currentStatus); } public Result doing(String activityId, Enum<Status> currentStatus) { return stateMap.get(currentStatus).doing(activityId, currentStatus); } }
itstack-demo-design-19-01
例子中還得傳兩個狀態來判斷。@Test public void test_Editing2Arraignment() { String activityId = "100001"; ActivityService.init(activityId, Status.Editing); StateHandler stateHandler = new StateHandler(); Result result = stateHandler.arraignment(activityId, Status.Editing); logger.info("測試結果(編輯中To提審活動):{}", JSON.toJSONString(result)); logger.info("活動信息:{} 狀態:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus())); }
測試結果
23:59:20.883 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果(編輯中To提審活動):{"code":"0000","info":"活動提審成功"} 23:59:20.907 [main] INFO org.itstack.demo.design.test.ApiTest - 活動信息:{"activityId":"100001","activityName":"早起學習打卡領獎活動","beginTime":1593694760892,"endTime":1593694760892,"status":"Check"} 狀態:"Check" Process finished with exit code 0
@Test public void test_Editing2Open() { String activityId = "100001"; ActivityService.init(activityId, Status.Editing); StateHandler stateHandler = new StateHandler(); Result result = stateHandler.open(activityId, Status.Editing); logger.info("測試結果(編輯中To開啓活動):{}", JSON.toJSONString(result)); logger.info("活動信息:{} 狀態:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus())); }
測試結果
23:59:36.904 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果(編輯中To開啓活動):{"code":"0001","info":"非關閉活動不可開啓"} 23:59:36.914 [main] INFO org.itstack.demo.design.test.ApiTest - 活動信息:{"activityId":"100001","activityName":"早起學習打卡領獎活動","beginTime":1593694776907,"endTime":1593694776907,"status":"Editing"} 狀態:"Editing" Process finished with exit code 0
@Test public void test_Refuse2Doing() { String activityId = "100001"; ActivityService.init(activityId, Status.Refuse); StateHandler stateHandler = new StateHandler(); Result result = stateHandler.doing(activityId, Status.Refuse); logger.info("測試結果(拒絕To活動中):{}", JSON.toJSONString(result)); logger.info("活動信息:{} 狀態:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus())); }
測試結果
23:59:46.339 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果(拒絕To活動中):{"code":"0001","info":"審覈拒毫不可執行活動爲進行中"} 23:59:46.352 [main] INFO org.itstack.demo.design.test.ApiTest - 活動信息:{"activityId":"100001","activityName":"早起學習打卡領獎活動","beginTime":1593694786342,"endTime":1593694786342,"status":"Refuse"} 狀態:"Refuse" Process finished with exit code 0
@Test public void test_Refuse2Revoke() { String activityId = "100001"; ActivityService.init(activityId, Status.Refuse); StateHandler stateHandler = new StateHandler(); Result result = stateHandler.checkRevoke(activityId, Status.Refuse); logger.info("測試結果(拒絕To撤審):{}", JSON.toJSONString(result)); logger.info("活動信息:{} 狀態:{}", JSON.toJSONString(ActivityService.queryActivityInfo(activityId)), JSON.toJSONString(ActivityService.queryActivityInfo(activityId).getStatus())); }
測試結果
23:59:50.197 [main] INFO org.itstack.demo.design.test.ApiTest - 測試結果(拒絕To撤審):{"code":"0000","info":"撤銷審覈完成"} 23:59:50.208 [main] INFO org.itstack.demo.design.test.ApiTest - 活動信息:{"activityId":"100001","activityName":"早起學習打卡領獎活動","beginTime":1593694810201,"endTime":1593694810201,"status":"Editing"} 狀態:"Editing" Process finished with exit code 0
有效流轉
和拒絕流轉
,不一樣的狀態服務處理不一樣的服務內容。ifelse
,代碼的結構也更加清晰易於擴展。這就是設計模式的好處,能夠很是強大的改變原有代碼的結構,讓之後的擴展和維護都變得容易些。單一職責
和開閉原則
,當你只有知足這樣的結構下才會發現代碼的擴展是容易的,也就是增長和修改功能不會影響總體的變化。1. 重學 Java 設計模式:實戰工廠方法模式「多種類型商品不一樣接口,統一發獎服務搭建場景」
2. 重學 Java 設計模式:實戰原型模式「上機考試多套試,每人題目和答案亂序排列場景」
3. 重學 Java 設計模式:實戰橋接模式「多支付渠道(微信、支付寶)與多支付模式(刷臉、指紋)場景」
4. 重學 Java 設計模式:實戰組合模式「營銷差別化人羣發券,決策樹引擎搭建場景」
5. 重學 Java 設計模式:實戰外觀模式「基於SpringBoot開發門面模式中間件,統一控制接口白名單場景」
6. 重學 Java 設計模式:實戰享元模式「基於Redis秒殺,提供活動與庫存信息查詢場景」
7. 重學 Java 設計模式:實戰備忘錄模式「模擬互聯網系統上線過程當中,配置文件回滾場景」