2013-05-04 162 views
1

我花了最後一天的時間來嘗試確定哪種模式最適合我的特定場景,並且我一直在折磨着狀態模式&策略模式。當我在互聯網上閱讀示例時,它非常有意義......但它是另一種嘗試將它應用於自己的問題的技巧。我會描述我的情況和我面臨的問題,希望有人能指出我正確的方向。面向對象程序設計問題 - 狀態設計模式

問題:我有一個具有不同同步狀態的基礎對象:即最新,舊,從未發佈,未發佈等。現在取決於對象在行爲中的狀態是不同的,例如,您無法獲取最新版本對於從未發佈的基礎對象。在這一點上,國家設計模式似乎是最適合的...所以我已經實施了它,現在每個州都有CanGetLatestVersion,GetLatestVersion,CanPublish,Publish等方法。

這一切似乎都很好。但是可以說你有10個不同的子對象派生自基類......我的解決方案被破壞了,因爲當爲每個狀態執行「發佈」方法時,它需要子對象中的屬性來實際執行操作,但是每個狀態只有對基礎對象的引用。我剛剛花了一些時間創建一個示例項目,說明我在C#中的問題。

public class BaseDocument 
{ 
    private IDocumentState _documentState; 

    public BaseDocument(IDocumentState documentState) 
    { 
     _documentState = documentState; 
    } 

    public bool CanGetLatestVersion() 
    { 
     return _documentState.CanGetLatestVersion(this); 
    } 

    public void GetLatestVersion() 
    { 
     if(CanGetLatestVersion()) 
      _documentState.CanGetLatestVersion(this); 
    } 

    public bool CanPublish() 
    { 
     return _documentState.CanPublish(this); 
    } 

    public void Publish() 
    { 
     if (CanPublish()) 
      _documentState.Publish(this); 
    } 

    internal void Change(IDocumentState documentState) 
    { 
     _documentState = documentState; 
    } 
} 

public class DocumentSubtype1 : BaseDocument 
{ 
    public string NeedThisData { get; set; } 
} 

public class DocumentSubtype2 : BaseDocument 
{ 
    public string NeedThisData1 { get; set; } 
    public string NeedThisData2 { get; set; } 
} 

public interface IDocumentState 
{ 
    bool CanGetLatestVersion(BaseDocument baseDocument); 
    void GetLatestVersion(BaseDocument baseDocument); 
    bool CanPublish(BaseDocument baseDocument); 
    bool Publish(BaseDocument baseDocument); 
    SynchronizationStatus Status { get; set; }  
} 

public class LatestState : IDocumentState 
{ 
    public bool CanGetLatestVersion(BaseDocument baseDocument) 
    { 
     return false; 
    } 

    public void GetLatestVersion(BaseDocument baseDocument) 
    { 
     throw new Exception(); 
    } 

    public bool CanPublish(BaseDocument baseDocument) 
    { 
     return true; 
    } 

    public bool Publish(BaseDocument baseDocument) 
    { 
     //ISSUE HERE... I need to access the properties in the the DocumentSubtype1 or DocumentSubType2 class. 
    } 

    public SynchronizationStatus Status 
    { 
     get 
     { 
      return SynchronizationStatus.LatestState; 
     } 
    } 
} 

public enum SynchronizationStatus 
{ 
    NeverPublishedState, 
    LatestState, 
    OldState, 
    UnpublishedChangesState, 
    NoSynchronizationState 
} 

我當時想過落實國家對每個子對象......這會工作,但我需要創建50類,即(10歲以下兒童×5種不同的狀態),而似乎只是絕對瘋了...爲什麼我在這裏!

任何幫助將不勝感激。如果它是混淆請讓我知道,所以我可以澄清!

Cheers

+0

你究竟是什麼意思?*「從基類派生的10個不同的子對象」*?這部分有點混亂。 – acdcjunior 2013-05-04 05:34:58

+0

我已經添加了一些示例代碼來幫助說明問題。在這種情況下,只有兩個子對象,即DocumentSubtype1&DocumentSubtype2 – Fred 2013-05-04 05:56:41

回答

0

讓我們重新思考這個問題。

1)你有一個本地'句柄',對一些你並不真正擁有的數據。 (其中一些存儲或發佈在其他地方)。

2)也許句柄,就是我們之前稱之爲「狀態」的 - 一個簡單的通用API,沒有實現細節。

3)而不是'CanPublish','GetLatestVersion'從BaseDocument委託給State - 聽起來像Handle應該委託給特定的DocumentStorage實現。

4)當代表外部國家或存儲位置,使用一個單獨的對象的是理想的,以包覆該新的/存在的/缺失狀態&標識符,在存儲位置。 5)我不確定'版本'是否是'發佈地點'的一部分;我不確定'版本'是否是'發佈地點'的一部分。或者如果他們是兩個獨立的存儲位置。我們的句柄需要爲每個獨立位置提供「存儲狀態」表示,它將存儲在哪個位置。

例如:

Handle 
    - has 1 LocalCopy with states (LOADED, NOT_LOADED) 
    - has 1 PublicationLocation with Remote URL and states (NEW, EXIST, UPDATE, DELETE) 

Handle.getVersions() then delegates to PublicationLocation. 
Handle.getCurrent() loads a LocalCopy (cached), from PublicationLocation. 
Handle.setCurrent() sets a LocalCopy and sets Publication state to UPDATE. 
    (or executes the update immediately, whichever.) 

遠程存儲位置/運輸可子類型用於訪問的不同的方法,或LocalCopy /文件可以爲亞型不同類型的內容。

這,我很確定,是更正確的解決方案。


[此前]保留「國家」有些從「文件」對象分開的(姑且稱之爲文件,因爲我們需要調用它東西 - 和你沒有指定)

從BaseDocument向下生成你的層次結構,擁有一個BaseDocument.State成員,並通過引用其Document實例來創建State對象 - 因此他們有權訪問&可以處理細節。

本質:

  • BaseDocument < --friend - >國家
  • 文檔亞型從BaseDocument繼承。
  • 受保護的方法&文檔heirarchy中的成員,使狀態可以做任何需要的操作。

希望這會有所幫助。

+0

感謝您的回覆......我不確定我確切地理解您的意思。我在原帖中添加了一些示例代碼......你能提供進一步的解釋嗎?乾杯 – Fred 2013-05-04 05:55:46

0

許多設計模式都可以用於這種架構問題。不幸的是,你沒有舉例說明你如何進行發佈。不過,我會說出一些好的設計:

  1. 把附加參數基本文檔,並使其 空。如果未在文檔中使用,則爲空。否則,它 有價值。這裏你不需要繼承。

  2. 請勿將發佈方法置於DocumentState,而應將其放入 BaseDocument。邏輯上,發佈方法必須是BaseDocument的 部分,而不是DocumentState。

  3. 讓其他服務類來處理髮布(發佈商 服務)。你可以通過使用抽象工廠模式來實現它。這種方式,你需要創建1:1的文件:發佈者對象。它可能是 很多,但您可以自由修改每個文檔的發佈者。

    public interface IPublisher<T> where T : BaseDocument 
    { 
        bool Publish(T document); 
    } 
    
    public interface IPublisherFactory 
    { 
        bool Publish(BaseDocument document); 
    } 
    
    public class PublisherFactory : IPublisherFactory 
    { 
        public PublisherFactory(
         IPublisher<BaseDocument> basePublisher 
         , IPublisher<SubDocument1> sub1Publisher) 
        { 
         this.sub1Publisher = sub1Publisher; 
         this.basePublisher = basePublisher; 
        } 
        IPublisher<BaseDocument> basePublisher; 
        IPublisher<SubDocument1> sub1Publisher; 
    
        public bool Publish(BaseDocument document) 
        { 
         if(document is SubDocument1) 
         { 
          return sub1Publisher.Publish((SubDocument1)document); 
         } 
         else if (document is BaseDocument) 
         { 
          return basePublisher.Publish(document); 
         } 
         return false; 
        } 
    } 
    
    public class LatestState : IDocumentState 
    { 
        public LatestState(IPublisherFactory factory) 
        { 
         this.factory = factory; 
        } 
        IPublisherFactory factory; 
    
        public bool Publish(BaseDocument baseDocument) 
        { 
         factory.Publish(baseDocument); 
        } 
    } 
    
  4. 使用Composition over inheritance。您爲每個狀態設計每個接口,然後在文檔中進行組合。總之,你可以有5個CanGetLatestVersion和其他組成類,但10個發佈者組成類。

  5. 更高級的和基於您使用的存儲庫,也許你可以使用Visitor pattern。這樣,您可以自由修改每個發佈方法。它與我的第3點類似,只是它在一個班級中被宣佈。例如:

    public class BaseDocument 
    { 
    
    } 
    public class SubDocument1 : BaseDocument 
    { 
    
    } 
    
    public class DocumentPublisher 
    { 
        public void Publish(BaseDocument document) 
        { 
    
        } 
        public void Publish(SubDocument1 document) 
        { 
         // do the prerequisite 
         Publish((BaseDocument)document); 
         // do the postrequisite 
        } 
    } 
    

可能有其他的設計可供選擇,但你怎麼訪問你的倉庫它是依賴於。