最近學完《Head first to Design Pattern》這本書,正好打算利用業餘時間重寫公司的一個項目,就想拿着重建項目的機會練下手,此文用來記錄下遇到的坑和解決方案。java
首先介紹下項目背景,一個很簡單的流程,對象是Project,有一個標記審批狀態的字段爲status,根據業務流程,轉到不一樣的階段,每一個階段對應一個值,很適合用狀態模式來實現。設計模式
貼一下設計模式書中的圖:裏面的術語下文要用到。app
首先先定義接口,並寫好實現類,而後把Context也寫好,基本上都是照抄書上代碼,毋庸多言。我惟一作了改動的是把State接口改爲了普通類,底下的ConcreateState用的覆蓋的方法。默認父類方法裏都是拋出UnsupportedOperationException。這樣若是調用子類沒有實現的方法時,會自動拋出不支持的操做。框架
貼一下個人類圖:ide
那麼這個狀態機是如何跟project的狀態掛接起來的呢,書中的糖果機只一個,而project有不少個,怎麼讓每一個project都能用上狀態模式呢?我繞了不少彎路,才發現以前的誤區,project就是糖果,status就是count,狀態機不須要有不少個,只要能處理這個狀態就好了。this
那麼context的構造器方法就能夠改形成傳入project對象的構造器設計
public StateContext(Project project) { initialState = new InitialState(this); approvalPendingState = new ApprovalPendingState(this); approvalFailState = new ApprovalFailState(this); biddingState = new BiddingState(this); bidFailState = new BidFailState(this); implementState = new ImplementState(this); finishState = new FinishState(this); closedState = new ClosedState(this); switch (project.getStatus()) { case Project.INITIAL: state = initialState; break; case Project.APPROVAL_PENDING: state = approvalPendingState; break; case Project.APPROVAL_FAIL: state = approvalFailState; break; case Project.BIDDING: state = biddingState; break; case Project.BID_FAIL: state = bidFailState; break; case Project.IMPLEMENT: state = implementState; break; case Project.FINISH: state = finishState; break; case Project.CLOSED: state = closedState; break; } }
而後把Project傳入每一個狀態類,code
public Project apply(Project project){ return state.apply(project); }
在狀態類內改變後再返回。 對象
@Override public Project apply(Project project) { project.setStatus(Project.APPROVAL_PENDING); stateContext.setState(stateContext.getApprovalPendingState()); return project; }
在Controller裏以下圖調用接口
try { StateContext stateContext = new StateContext(project); projectService.save(stateContext.apply(project)); addMessage(redirectAttributes, "保存項目成功"); } catch (UnsupportedOperationException e) { addMessage(redirectAttributes, "當前階段不支持此操做"); }
以前我曾經想過把Service直接放到每一個Concrete動做類裏去,可是存在的問題是無法注入Service,由於都是構造方法構造出來的,而後我又改造構造方法,所有換成注入的,可是在注入Context裏保存當前狀態的state對象時遇到問題了,state是可變的,無法肯定應該注入哪個。後來全又改回書上的寫法了。
另外狀態模式也有框架,是在sprngboot下的,之後有機會能夠嘗試一下。最後寫流程仍是提倡使用工做流,使用狀態模式的問題是類爆炸太多,寫起來也不方便。