2010-01-07 75 views
2

我想模擬一個特定的過程,我認爲狀態模式可能是一個很好的匹配。我希望得到你的反饋,但是關於國家能否滿足我的需求以及它應該如何與我的持久性機制相結合。狀態模式對只讀狀態有用嗎?

我有一個包含多個對象的CMS,例如Pages。這些對象(我們將使用頁面的例子,但它的大多數對象true)可以在多個狀態之一,3個例子是: 未發佈 發佈 返工

當出版,它們是可編輯的。一旦發佈,它們不可編輯,但可以移入重做狀態。在Reworking狀態下,它們可以再次編輯,並可以重新發布。

顯然,這些頁面是否可編輯的決定應該在模型本身而不是UI中。所以,國家模式突然出現了。但是,如何防止將值分配給對象的屬性?這似乎是一個壞主意,請檢查每個屬性設置器:

if (!CurrentState.ReadOnly) 

任何想法如何工作?有更好的模式嗎?

回答

1

更新:

其實我今天上午將與您的特定情況下工作,是一個更容易保持想到了一個方法。我將在這裏留下原始的兩點,但我會推薦最後的選項,所以請跳到「更好的方法」部分。

  1. 創建一個ThrowIfReadOnly方法,它在罐子上做什麼。這個重複性稍低,避免了嵌套。

  2. 使用接口。有一個IPage實現您想要的功能,使每個公共方法返回IPage,然後有兩個實現,一個EditablePage和一個ReadOnlyPage。只要有人試圖修改它,ReadOnlyPage只會引發異常。還要在IPage接口上放置IsReadOnly屬性(或State屬性),以便消費者可以實際檢查狀態而不必捕捉異常。

選項(2)的更多或更少的如何IListReadOnlyCollection<T>一起工作。這樣可以節省您在每個方法開始時進行檢查的麻煩(從而消除了忘記驗證的風險),但需要您維護兩個類。

- 更好的方法 -

一個適當的技術規範將有很大的幫助澄清這一問題。我們在這裏真正擁有的是:

  • A 系列任意「寫」動作;
  • 每個動作具有相同結果,依賴於國家:
  • 要麼採取動作(未發表/再加工),或失敗/(只讀)空操作。

真的有什麼需要抽象是沒有這麼多的行動本身,但執行說行動。因此,功能性善良的點點將幫助我們在這裏:

public enum PublishingState 
{ 
    Unpublished, 
    Published, 
    Reworking 
} 

public delegate void Action(); 

public class PublishingStateMachine 
{ 
    public PublishingState State { get; set; } 

    public PublishingStateMachine(PublishingState initialState) 
    { 
     State = initialState; 
    } 

    public void Write(Action action) 
    { 
     switch (State) 
     { 
      case PublishingState.Unpublished: 
      case PublishingState.Reworking: 
       action(); 
       break; 
      default: 
       throw new InvalidOperationException("The operation is invalid " + 
        "because the object is in a read-only state."); 
     } 
    } 
} 

現在它變得幾乎微不足道寫的類本身:

public class Page 
{ 
    private PublishingStateMachine sm = new 
     PublishingStateMachine(PublishingState.Unpublished); 

    private string title; 
    private string category; 

    // Snip other methods/properties 
    // ... 

    public string Title 
    { 
     get { return title; } 
     set { sm.Write(() => title = value; } 
    } 

    public string Category 
    { 
     get { return category; } 
     set { sm.Write(() => category = value; } 
    } 

    public PublishingState State 
    { 
     get { return sm.State; } 
     set { sm.State = value; } 
    } 
} 

這不僅會更多或更少的貫徹落實國家模式,但不需要爲不同的狀態維護單獨的類或甚至單獨的代碼路徑。例如,如果您想要將InvalidOperationException變爲無操作,只需從Write方法中刪除throw語句。或者,如果你想添加一個額外的狀態,如Reviewing或類似的東西,你只需要添加一條case行。

這不會爲你處理或任何真正複雜的動作是做取決於國家(不僅僅是「成功」或「失敗」等)不同的事物狀態轉換,但它聽起來並不像你需要的。所以這給你一個插入狀態的實現,它幾乎不需要額外的代碼來使用。

當然,還是有依賴注入/ AOP的選擇,但顯然還有很多與該方法相關的開銷,我可能不會用它的東西那麼簡單。

+0

謝謝,我在想選項2,它可以很好地工作 – hackerhasid 2010-01-07 22:24:04

+0

我想這就是我一直在尋找的答案。我非常喜歡這個語法,謝謝! – hackerhasid 2010-01-11 14:59:49

3

使用維基百科的Java example,該結構具有一個背景下,這就要求在基本狀態定義的方法,其具體狀態覆蓋。

State Diagram

在你的情況下,上下文是類似的頁面。在一些州,edit()方法只是一個沒有操作。上下文中的某些操作可能會隱式執行狀態更改。有沒有在客戶端代碼中任何需要測試你是在狀態。

+0

是的,但Pages有一組屬性和其他對象,說Module,有其他對象。你將如何處理? – hackerhasid 2010-01-07 22:20:14

+0

我認爲狀態模式是處理工作流狀態的好主意。這幾乎是問題的範圍。我認爲超出這個範圍的設計問題應該得到他們自己的問題。特別是,「模塊」是需要定義的那些實用詞之一。 – 2010-01-07 23:21:03

+0

+1 for *在客戶端代碼中永遠不需要測試你所處的狀態。* I.E.如果存在,模式沒有正確實施。 – radarbob 2012-06-05 19:50:35