2012-10-25 54 views
2

我需要一些關於如何構建一個充當Builder的流暢接口的建議,負責根據調用的方法返回不同的具體類型。Fluent接口構建不同的混凝土類型

想象一下,我需要使用我的ProductBuilder(流利地)創建以下類型之一:Product,ServiceProduct,PackagedProduct(都來自Product)。

我想用這樣的語法通順(其他建議十分歡迎更多):

要創建一個產品:

var product = new ProductBuilder() 
    .Create("Simple product") 
    .WithPrice(12.5) 

要創建ServiceProduct

var product = new ProductBuilder() 
    .Create("Service product") 
    .WithPrice(12.5) 
    .AsServiceProduct() 
     .ServiceProductSpecificMethods...() 

和PackagedProduct調用AsPackagedProduct()而不是AsServiceProduct()等。你明白了。

我還沒有找到一個示例,顯示此最佳做法。只有最終構建返回相同類型的示例。

有什麼建議嗎?

+0

您對此接口有一整套要求嗎?你是否在尋求幫助將他們組織成一個流暢的界面? –

+1

什麼是建築師模式真的在這裏買你?你想解決什麼樣的問題? –

+0

@GlennFerrieLive:我想知道如何打電話給AsServiceProduct()「通知」ProductBuilder上的Build方法,以實際創建具有特定屬性的ServiceProduct實例(由構建器上的後續方法調用提供,在AsServiceProduct()之後),而不是以最簡單形式的Product。我可以在AsServiceProduct方法中設置一個私有屬性,告訴構建者創建一個ServiceProduct而不是一個Product,但我認爲有比這更好的解決方案。 –

回答

3

我在這裏看到兩個選項。

如果有那是固定的,而不是旨在延長產品數量有限,那麼只需要創建一個Create方法爲每個產品:

var product = new ProductBuilder() 
    .CreateSimple() 
    .WithPrice(12.5); 

var product = new ProductBuilder() 
    .CreateService() 
    .WithPrice(12.5) 
    .ServiceProductSpecificMethods...(); 

如果你不想(或者可以」噸有)ProductBuilder知道所有類型的產品,那麼我會使用泛型:

public class Product {} 
public class SimpleProduct : Product {} 
public class ServiceProduct : Product {} 

var product = new ProductBuilder<SimpleProduct>() 
    .WithPrice(12.5); 

下面是設計的起點遵循:

public class Product 
{ 
    public decimal Price { get; set; } 
} 
public class SimpleProduct : Product { } 
public class ServiceProduct : Product 
{ 
    public string Service { get; set; } 
} 

public class ProductBuilder<T> where T : Product, new() 
{ 
    private List<Action<T>> actions = new List<Action<T>>(); 

    public T Build() 
    { 
     T product = new T(); 
     foreach (var action in actions) 
     { 
      action(product); 
     } 

     return product; 
    } 
    public void Configure(Action<T> action) 
    { 
     actions.Add(action); 
    } 
} 

public static class ProductExtensions 
{ 
    public static ProductBuilder<T> WithPrice<T>(this ProductBuilder<T> builder, decimal price) 
     where T : Product 
    { 
     builder.Configure(product => product.Price = price); 
     return builder; 
    } 

    public static ProductBuilder<T> WithService<T>(this ProductBuilder<T> builder, string service) 
      where T : ServiceProduct 
    { 
     builder.Configure(product => product.Service = service); 
     return builder; 
    } 
} 
+0

這是一個有趣的解決方案Servy。非常感謝你。但是,它要求產品屬性的設置者是公開/受保護的。你有沒有看到避免這種情況的方法? –

+0

@TommyJakobsen好吧,簡單的方法是將所有導致變異的東西都變成「內部」,並將所有這些變成它自己的.dll。爲了避免這種情況,你最終可能只會返回接口,而不是具體的對象,或者使用反射(這樣你可以使setter成爲私有的,並且仍然可以在類之外訪問它們)。 – Servy

+0

害怕回答:)但是,我明白了爲什麼,並且很可能沒有辦法解決它。 –

1

如果我給你正確的我會在這裏使用仿製藥,所以我可以寫類似:

var product = new ProductBuilder() 
.Create<Serviceproduct>() 
.WithPrice(12.5) 
    .ServiceProductSpecificMethods...() 

您也可以調用特定的服務方法,所以它實際上創造的最終產品之前添加構建方法

var product = new ProductBuilder() 
.Create<Serviceproduct>() 
.WithPrice(12.5) 
.Build() 
    .ServiceProductSpecificMethods...() 
+0

Create ()方法返回什麼?在第二個例子中,對Build的調用是否返回ServiceProduct的一個實例?在這種情況下,您正在構建無效的ServiceProduct,因爲在構建ServiceProduct之前需要在構建器上設置必需的屬性(通過方法鏈接),以確保構建器始終構建有效的產品。 –

+0

對不起,以便延遲迴復。第一種(簡單)方法的想法是,您可以在.Create ()調用上創建Serviceproduct,並使用擴展方法來配置產品,然後調用特定的方法。擴展方法應該有明確的限制才能正確工作。 –

+0

在第二個示例中,構思與@Servy的回答非常相似。 ProductBuilder()。創建()將返回類ProductBuilder (可從answer @Servy中獲取)。實際上唯一的區別是你將有新的ProductBuilder()。創建()。WithPrice(12.5).Build()vs New ProductBuilder ()。WithPrice(12.5).Build()。你將會在我的實施中增加一個額外的課程。 –