2012-08-31 49 views
5

我創建了一個流暢的建設者風格圖案與數據加載我的測試幫助。某些方法的順序很重要,並且想知道管理正確順序的首選方法是什麼。在建造者模式中管理訂單的首選方式是什麼?

我此刻的以下內容:

using NUnit.Framework; 

[TestFixture] 
public class DataBuilderTests 
{ 
    [Test] 
    public void Can_NAME() 
    { 
     new DataLoader() 
      .Start() // must be called first 
      .Setup() // then called next 
      .LoadEmployees() // optional order not NB 
      .LoadProducts() // optional order not NB 
      .StartCleanup() // begin cleanup 
      .CleanupEmployees() // optional order not NB 
      .CleanupProducts() // optional order not NB 
      .End(); 
    } 
} 

public class DataLoader 
{ 
    public DataBuilderSetup Start() 
    { 
     return new DataBuilderSetup(this);  
    } 
} 

public class DataBuilderSetup 
{ 
    private readonly DataLoader _dataLoader; 

    public DataBuilderSetup(DataLoader dataLoader) 
    { 
     _dataLoader = dataLoader; 
    } 

    public DataBuilderOptions Setup() 
    { 
     // do setup 
     return new DataBuilderOptions(_dataLoader); 
    } 
} 

public class DataBuilderOptions 
{ 
    private readonly DataLoader _dataLoader; 

    public DataBuilderOptions(DataLoader dataLoader) 
    { 
     _dataLoader = dataLoader; 
    } 

    public DataBuilderOptions LoadEmployees() 
    { 
     // load 
     return this; 
    } 

    public DataBuilderOptions LoadProducts() 
    { 
     // load 
     return this; 
    } 

    public DataBuilderCleanupOptions StartCleanup() 
    { 
     return new DataBuilderCleanupOptions(_dataLoader); 
    } 
} 

public class DataBuilderCleanupOptions 
{ 
    private readonly DataLoader _dataLoader; 

    public DataBuilderCleanupOptions(DataLoader dataLoader) 
    { 
     _dataLoader = dataLoader; 
    } 

    public DataBuilderCleanupOptions CleanupEmployees() 
    { 
     // cleanup 
     return this; 
    } 

    public DataBuilderCleanupOptions CleanupProducts() 
    { 
     // cleanup 
     return this; 
    } 

    public DataLoader End() 
    { 
     return _dataLoader; 
    } 
} 
+0

你目前的解決方案有什麼問題? –

+0

沒什麼,只是我今天早上想出來的,對其他人如何處理場景感到好奇 – Chev

+0

如果調用LoadEmployees,就必須調用「CleanupEmployees」嗎? –

回答

0

的首選方法是避免它不惜一切代價。設計你的建設者,因此它明顯需要做什麼。

ObjectBuilder 
.Init() 
.Optional1() 
.Optional3() 
.Optional2() 
.ToObject() 

如果需要首先發生什麼事,那麼在構造函數或工廠方法中執行此操作。然後總是有一個方法完成構建過程,清理和所有。

+0

我現在這樣做,但想象一下如果使用先前的選擇,選擇可能只存在的情況。典型場景是具有ASC或DESC選項的常見OrderBy子句。這些選項僅在選擇OrderBy時才存在。 – Chev

+0

@Chev - 這實際上是一個非常不同的場景。你所描述的實際上是鏈接建設者。在這種情況下,您將無法添加新的謂詞,因爲您正在處理新的構建器。建設的道路將保持明確。 – ChaosPandion

1

您可以將專用隊列成員添加到建設者,例如Queue<string>,並在每一個建設者步驟中添加的操作的名稱。

.Build()方法或在您的情況.End()將檢查隊列包含在一個正確的順序正確的操作名稱。如果沒有,你可以拋出一個InvalidOperationException

您也可以用一棵樹作爲數據結構。一棵樹將允許您剖析在以前的構建器步驟中不可用的選項。

但最好考慮其他的答案是我的做法實際上是一個黑客,它會創建一個維護問題。

2

的生成器模式的優勢的一部分是,它可以保護消費者免受方法調用的規定「魔力」的排序。

我建議改變你的設計,使之一:提供了前屏蔽消費者

  • 所有必需的參數從執行命令StartSetup電話。
  • 修改您的實體的責任,讓他們可以隨意建立。

顯然這是我的個人喜好。如果這種類型構成了第三方將使用的庫的一部分,那麼我強烈建議不要使用魔術方法排序。如果這隻可能在內部使用,則由您來衡量與更改代碼相關的成本,而不是這樣做。

+0

有趣的是,我開始思考的第三方庫如FluentNHibernate,FluentValidation和NHibernate本身似乎這樣做。 – Chev

+0

同意,它擊敗了構建器必須以特定順序調用方法來配置它的一個主要目的。構建器應該緩存這些部分,然後在最終的Build調用中對其進行排序。 – tcarvin

0

在Java(C#和它的多重繼承不應使任何不同),你可以這樣做的:

聲明的接口集,僅包含一個方法:

Interface DoFirstThing { // could be renamed to "BuilderOnStart" or "BuilderStartingState" 
    DoSecondThing doFirst(); 
} 

Interface DoSecondThing { 
    DoLastThing doSecond(); 
} 

Interface DoLastThing { 
    BuilderReady doLast(); 
} 

Interface BuilderReady { 
    Result build(); 
} 

class BuilderWithForcedSequence implements DoFirstThing, DoSecondThing, DoLastThing, BuilderReady { 

    // implement all 

} 

和最後你會需要一些工廠或工廠方法來設置初始狀態爲建設者:

public DoFirstThing createNewBuilderWithForcedSequence(requiredParameters){ 
    return new BuilderWithForcedSequence(requiredParameters); 
} 

這將產生生成器,以建設甲基的強制排序ods(它們應該從doThat改名爲有意義的東西),那可以只調用doFirst(),然後doSecond() ...等等,直到最終狀態,當obiect被構建時,使用build()方法。

Result result = builder.doFirst().doSecond().doLast().build(); 


編輯:
哎呀,那是相當準確的方法:)

0

您當前的解決方案是,我想借此提供一個流暢的語法,雖然我不一定會說這完全辦法遵循建造者模式。從本質上講,你所做的就是將構建者與狀態機提供的限制聯繫在一起。我看到你在做的事情上與其他普遍接受的流暢配置(例如流利的hibernate或流暢的斷言)相比沒有什麼區別。

相關問題