個人BO
1-個人BO之強類型
2-個人BO之數據保護
3-個人BO之狀態控制
4-個人BO之導航屬性html
信息管理系統(MIS)經常有流程,一個流程由多個環節構成,不一樣的環節的流轉經過狀態控制。好比簡單的購物流程:
對應着這樣的狀態:前端
結合起來就是狀態圖:
java
狀態的控制在MIS中每每起着舉足輕重的做用,如先付款才能退款。若沒有控制好狀態,未付款就能退款,實在荒唐。express
此作法比較簡單,前端根據狀態顯示不一樣的界面和操做,提交時後端也不判斷狀態,直接認爲當前確定處於正確的狀態,不然前端也不會提交此請求。這種作法不安全,攻擊者能夠自行發起請求,繞過界面,不顧當前的狀態。原本這麼不靠譜的方案不足拿出來講,可是因爲種種「現實緣由」現實中時而有見此方案。拿出來講是想說明存在這樣的方案,但要避免這麼幹。後端
通常的作法,是在業務邏輯中進行判斷。具體來講就是要作具體的業務操做前判斷當前的狀態,只有當前的狀態與預期的一種或多種狀態符合時才進行後續的操做。最後更新狀態。這種作法,軟件寫得對不對,全靠人當時的精神狀態。從工程學來說,人是不可靠的。本作法潛在的風險:安全
中等複雜的業務流程的狀態圖:
數據結構
集中管理所有狀態。首先把可能的變化作成狀態圖(State Diagram/state transition diagram/狀態轉移圖,這幾個名是否指同個東西?請懂的人賜教), 凡是狀態圖沒有出現的都是非法。當狀態值變化時,在狀態圖中查找舊值可否直接到達新值。若舊值沒法直達新值,說明不是非法調用,就是程序寫錯了。
狀態圖須要轉化成數據結構才方便計算機的處理,通常是多個二元組,
每一個二元組是這樣的:(舊狀態值,新狀態值),意思是 舊狀態值
容許直接變成新狀態值
。好比簡單的購物流程,即轉化爲這樣的結構:this
(開始, 已下單) (已下單, 已付款) (已付款, 已發貨) (已發貨, 已確認收貨) (已確認收貨, 已評價)
這個狀態圖我是寫死在代碼中,也能夠存儲在任何位置,只要運行時能裝載到內存便可。lua
不管當前作什麼具體的業務操做,只要狀態發生變化,就調用檢查,從而能防止非法的狀態變化。結合前面介紹過的個人BO之數據保護就能全面地防止非法的流程的出現。code
Java
// 訂單 public class OrderBo extends BoBase { Order order; public Long getId() { return order.getId(); } protected void setId(Long id) { /* 每一個 set 都不是 public */ order.setId(id); setTrackUpdate(); /* 父類方法,後續文章會介紹 */ } // 這裏省略若干屬性 // 訂單狀態 public OrderStatus getStatus() { String sStatus = order.getStatus(); return OrderStatus.valueOf(sStatus); } protected void setStatus(OrderStatus status) { OrderStatus.statusChanging(this.getStatus(), status); String sStatus = status.toString(); order.setStatus(sStatus); setTrackUpdate(); } // 發貨 public void delivery(String expressCompanyName, String expressNumber) { this.setStatus(OrderStatus.已發貨); // 你沒有看錯,這裏能夠不判斷當前的狀態 this.setExpressCompanyName(expressCompanyName); this.setExpressNumber(expressNumber); // 其它各類操做 this.save(); /* 父類方法,後續文章會介紹 */ } } public enum OrderStatus{ 開始, 已下單, 已付款, 已發貨, 已確認收貨, 已評價; public static void statusChanging(OrderStatus oldStatus, OrderStatus newStatus) { switch (oldStatus) { case 開始: switch (newStatus) { case 已下單: return; } break; case 已下單: switch (newStatus) { case 已付款: return; } break; case 已付款: switch (newStatus) { case 已發貨: return; } break; case 已發貨: switch (newStatus) { case 已確認收貨: return; } break; case 已確認收貨: switch (newStatus) { case 已評價: return; } break; case 已評價: break; } throw CommonException.invalidStatus("訂單" + this.getEvaluateStatus() + ",不容許此操做"); } }
將來可能在二元組中加入「動做」,變成三元組:(舊狀態值, 動做, 新狀態值)。能在狀態變化時加上動做一塊兒判斷,效果更好。
感謝 螺絲釘 協助閱稿並提出修改建議。