2012-10-25 25 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()。你将会在我的实施中增加一个额外的课程。 –