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

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

背景

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

思路

    因爲孤陋寡聞,在獲得需求以後,我第一反應不是「工做流」,而是「狀態機」。它從「提交」狀態開始,流經「已初審」「已終審」或者「初審駁回」「終審駁回」等狀態,進入終態。
    這個狀態機以下圖所示:
p_w_picpath
    固然,狀態機中狀態的名稱、狀態間的流轉,是與業務需求緊密相關的。例如,有些業務會要求在「已終審」狀態下執行「駁回」操做後進入「終審駁回」狀態,而有些則要求返回「已初審」狀態。不過萬變不離其宗,種種流程最終都能概括到「狀態機」中來。
    在這個思路下,我用了兩種不一樣的設計模式來實現需求——狀態模式和策略模式,它們都很好的完成了任務。須要多說一句的是,這是兩個不一樣系統下獨立的兩次實現,而不是一個系統中的「原始版」和「改進版」。於是,兩個方案之間並無很是顯著的優劣對比,本文的重點也不是兩者的「優劣」對比。設計模式

方案一:狀態模式

  • 狀態模式
        首先來回顧一下咱們常說的狀態模式。簡便起見,這裏只提供類圖。
        p_w_picpath
        其中的核心是「狀態」接口。這個接口中有N個方法,對應的是狀態機中的N個狀態。每一個方法負責從當前狀態遷移到另外一個狀態上——通常是別的狀態,也能夠仍然是當前狀態。
        每一個具體的狀態都繼承自這個接口,並在實現類中封裝本身所須要的數據、重寫本身的狀態遷移操做。app

  • 個人方案
        在個人設計方案中,類圖則是這個樣子的:
    p_w_picpath
        與「教科書」上的類圖類似的,是「狀態」接口(Examiner),以及各個實際狀態所對應的子類。
        與之不一樣的是,雖然個人狀態機中有五個狀態,可是因爲每一個狀態最多都只有兩個狀態遷移操做(經過,或者駁回),所以,狀態接口中我只定義了兩個方法。
        還有一點不一樣在於,我在Examiner接口下,加了一個默認的實現類(ExaminerAsDefault)。這個類實際上什麼都不作,每一個方法都直接拋出UnSupportedOperationException。這個類的做用是簡化子類,使得每一個子類只須要重寫本身關心的方法,而不須要重寫無關方法。固然,Java 8爲接口引入的默認方法,能夠實現一樣的功能,這是後話。此外,因爲業務需求中每次只作一步狀態遷移,所以Examiner接口不須要再返回本身。還有一點不一樣的是,這個方案中,狀態遷移操做與狀態數據被拆開了——遷移操做由Examiner定義,狀態數據則用Dto來封裝。框架

  • 擴展
        當出現新的狀態、或者新的遷移操做怎麼辦呢?
        出現新的狀態時,建立一個新的「狀態」子類,並實現對應的「狀態遷移」方法就好了。出現新的遷移操做時則更簡單,只須要作第二步就能夠了。ide

方案二:策略模式

  • 策略模式
        衆所周知的策略模式通常都有這樣的類圖:
    p_w_picpath工具

  • 個人方案
        在個人設計方案中,類圖則是這樣的:
        p_w_picpath
        能夠說這是一個「標準」的策略模式類圖。接口定義從一個狀態到另外一個狀態的遷移動做,不一樣的子類用不一樣的「策略」去實現它——例如從「已提交」到「已初審」,或者從「已初審」到「初審駁回」,等等。
        狀態相關的數據,仍然由單獨的Dto來保存和傳遞。學習

  • 擴展
        策略模式下,如何增長新的狀態、新的遷移操做呢?
        因爲策略模式僅僅定義了「狀態遷移」動做,所以,不管是增長新的狀態、仍是增長新的遷移操做,都只須要增長對應的子類便可。spa

對比

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

相關文章
相關標籤/搜索