1. 程式人生 > >狀態與策略——審批操作的兩種方案

狀態與策略——審批操作的兩種方案

    審批操作是ERP或OA系統中必不可少的功能之一。這裡介紹兩種我設計的用於審批操作的方案,並藉此就“狀態模式”與“策略模式”提出一點自己的理解。
    別問我為什麼不使用工作流引擎等工具來實現審批功能。做第一版方案時,我孤陋寡聞得並不知道有這個東西。後來引入工作流框架會導致學習曲線驟然上揚,不太划算。

背景

    背景無需過多介紹,不外乎有一些資料/任務/請求,需要由領導們點一下頭或者按鈕。

思路

    由於孤陋寡聞,在得到需求之後,我第一反應不是“工作流”,而是“狀態機”。它從“提交”狀態開始,流經“已初審”“已終審”或者“初審駁回”“終審駁回”等狀態,進入終態。
    這個狀態機如下圖所示:

image
    當然,狀態機中狀態的名稱、狀態間的流轉,是與業務需求緊密相關的。例如,有些業務會要求在“已終審”狀態下執行“駁回”操作後進入“終審駁回”狀態,而有些則要求返回“已初審”狀態。不過萬變不離其宗,種種流程最終都能歸納到“狀態機”中來。
    在這個思路下,我用了兩種不同的設計模式來實現需求——狀態模式和策略模式,它們都很好的完成了任務。需要多說一句的是,這是兩個不同系統下獨立的兩次實現,而不是一個系統中的“原始版”和“改進版”。因而,兩個方案之間並沒有非常顯著的優劣對比,本文的重點也不是二者的“優劣”對比。

方案一:狀態模式

  • 狀態模式
        首先來回顧一下我們常說的狀態模式。簡便起見,這裡只提供類圖。
        image
        其中的核心是“狀態”介面。這個介面中有N個方法,對應的是狀態機中的N個狀態。每個方法負責從當前狀態遷移到另一個狀態上——一般是別的狀態,也可以仍然是當前狀態。
        每個具體的狀態都繼承自這個介面,並在實現類中封裝自己所需要的資料、重寫自己的狀態遷移操作。

  • 我的方案
        在我的設計方案中,類圖則是這個樣子的:

    image
        與“教科書”上的類圖相似的,是“狀態”介面(Examiner),以及各個實際狀態所對應的子類。
        與之不同的是,雖然我的狀態機中有五個狀態,但是由於每個狀態最多都只有兩個狀態遷移操作(通過,或者駁回),因此,狀態介面中我只定義了兩個方法。
        還有一點不同在於,我在Examiner介面下,加了一個預設的實現類(ExaminerAsDefault)。這個類實際上什麼都不做,每個方法都直接丟擲UnSupportedOperationException。這個類的作用是簡化子類,使得每個子類只需要重寫自己關心的方法,而不需要重寫無關方法。當然,Java 8為介面引入的預設方法,可以實現同樣的功能,這是後話。此外,由於業務需求中每次只做一步狀態遷移,因此Examiner介面不需要再返回自己。還有一點不同的是,這個方案中,狀態遷移操作與狀態資料被拆開了——遷移操作由Examiner定義,狀態資料則用Dto來封裝。

  • 擴充套件
        當出現新的狀態、或者新的遷移操作怎麼辦呢?
        出現新的狀態時,建立一個新的“狀態”子類,並實現對應的“狀態遷移”方法就行了。出現新的遷移操作時則更簡單,只需要做第二步就可以了。

方案二:策略模式

  • 策略模式
        眾所周知的策略模式一般都有這樣的類圖:
    image

  • 我的方案
        在我的設計方案中,類圖則是這樣的:
        image
        可以說這是一個“標準”的策略模式類圖。介面定義從一個狀態到另一個狀態的遷移動作,不同的子類用不同的“策略”去實現它——例如從“已提交”到“已初審”,或者從“已初審”到“初審駁回”,等等。
        狀態相關的資料,仍然由單獨的Dto來儲存和傳遞。

  • 擴充套件
        策略模式下,如何增加新的狀態、新的遷移操作呢?
        由於策略模式僅僅定義了“狀態遷移”動作,因此,無論是增加新的狀態、還是增加新的遷移操作,都只需要增加對應的子類即可。

對比

    我並不喜歡比較不同設計模式之間的區別。但這裡仍可以多說幾句。
    用狀態模式實現狀態機,大概是一個最直觀、最容易想到的設計。但是,標準的狀態模式將狀態資料也封裝到狀態類中。這使得這個類無法用單例實現。另外,由於狀態介面中,對應每一個狀態都有一個方法,這可能會使得部分子類非常的大。
    用策略模式實現狀態機,與狀態機思想是有衝突的。狀態機是以“狀態”為本,狀態遷移操作為輔;而策略模式卻專注於狀態遷移操作,“狀態”的概念淡化得幾乎消失了。此外,與狀態模式中的“超級類”相反,策略模式可能導致“類爆炸”。
    兩種模式之間的分界線,也許只是概念上的“以狀態為本”或“以操作為本”。就實踐上來說,像我的方案中那樣,將狀態模式中的資料與操作拆分開,那麼整個方案與策略模式其實相去無幾。
    這是我不喜歡比較不同設計模式之間區別的原因。由於設計模式的變化、組合非常多,很多時候不同設計模式之間的界限僅僅存在於概念上、思想上,而不在實踐中。費盡心思去分析“如何區分23種設計模式”,只在學習階段有一點意義。我們更應該關注設計模式適用的業務場景、業務問題,以及如何實現它們。
    畢竟,科學可以滿足於“認識世界”,技術必須要以“改造世界”為目標。

參考文獻:

http://blog.51cto.com/winters1224/1959649