【问题标题】:Design Pattern: Builder设计模式:建造者
【发布时间】:2017-08-02 21:55:36
【问题描述】:

我已经寻找 Builder 模式(在 C# 中)的 good 示例,但找不到一个,因为我不了解 Builder 模式,或者我是试图做一些从未想过的事情。例如,如果我有一个抽象汽车和抽象构建器方法来创建汽车零件,我应该能够将我所有的 30 个选择发送给 Director,让它构建我需要的部件,然后构建我的汽车。无论生产哪种汽车、卡车、半自动等,我都应该能够以完全相同的方式“驾驶”它。

第一个问题是大多数示例硬编码属性值到具体部分,我真的认为应该来自数据库。我认为我的想法是将我的选择发送给主管(从数据源)并让构建器根据我的数据创建定制产品。

第二个问题是我希望构建器方法实际创建部件然后将它们分配给产品,而不是传递字符串而是真正的强类型产品部件。

例如,我想通过 Builder 为我制造表单字段来动态创建表单,包括标签、输入部分、验证等。这样我可以从我的 ORM 中读取对象,查看对象的元数据,将其传递给我的 Builder 并将新创建的用户控件结果添加到我的 Web 表单。

但是,我发现的每个 Builder 示例都只有硬编码数据,而不是将选择从主代码传递给 Builder 并推出定制产品。一切似乎都是一个很大的静态案例陈述。例如,如果我有三个参数,每个参数有 10 个选项,我不想构建 30 个具体的 Builder 方法,我只想创建足以制造我的产品所需的属性,可能只有三个。

我很想让 Director 仅存在于主代码中。应该有一种方法可以自动确定调用哪个具体的构建器方法,类似于多态性和方法重载(尽管这是一个非常糟糕的例子),而不是在模式中使用 case 语句。 (每次需要添加新的产品类型,都需要修改现有的Director,这样不好)。

【问题讨论】:

  • 也许,构建器模式中缺少的是对象最终不会被销毁,因此如果您不了解该模式,我建议稍后添加...
  • 不是您真正想要的,但请查看以下文章:
    the pattern description
    c# implementation sample
    希望这会有所帮助。

标签: c# design-patterns interface-builder builder


【解决方案1】:

BuilderPattern 的调用大多如下所示:

Car car = new CarBuilder().withDoors(4).withColor("red").withABS(true).build();

【讨论】:

  • 这并不是设计模式所描述的真正的建造者模式。该模式旨在创建相同源的不同表示。例如,一个编译器使用一个解析器,但对 x86、x64 和 Java 字节码有不同的后端。
  • 它可能与设计模式所描述的不完全一样,但可以确保创建的对象具有正确设置的所有属性。您还可以让对象生成器使用流畅的界面......但那是另一个问题
【解决方案2】:

我从来没有这样想过,但是 LINQ(模式,而不是语法)实际上是一个构建器,对吧?

它是一个流畅的界面,可以构建查询并可以创建不同表示形式的查询(SQL、内存中对象查询、Web 服务查询,Bart de Smet 甚至编写了 Linq-to-Excel 的实现)。

【讨论】:

    【解决方案3】:

    我将参考维基百科文章here中的C#示例。

    第一个问题是大多数示例硬编码属性值到具体部分,我真的认为应该来自数据库。我认为我的想法是将我的选择发送给主管(从数据源)并让构建器根据我的数据创建定制产品。

    在这种情况下,您将拥有实现 PizzaBuilder 的类,该类知道如何从数据库中检索数据。您可以通过多种方式进行操作。

    可以制作一个 HawaiianPizzaBuilder。当类初始化时,它会在数据库中查询夏威夷比萨饼并检索该行。然后,当调用各种 Build(x) 方法时,它会将属性设置为检索到的数据库行的相应字段。

    另一种方法是创建一个 PizzaDatabaseBuilder,并确保在初始化该类时向其传递该类型披萨所需的行 ID。例如,而不是

    waiter.PizzaBuilder = new HawaiianPizzaBuilder();
    

    你使用

    waiter.PizzaBuilder = new PizzaDatabaseBuilder("Hawaiian");
    

    第二个问题是我希望构建器方法实际创建部件然后将它们分配给产品,而不是传递字符串而是真正的强类型产品部件。

    应该不是问题。您需要的是其他 Factory/Builder 类型模式来初始化 Pizza 的字段。例如

    而不是

     public override void BuildDough()   { pizza.Dough   = "pan baked"; }
    

    你会做类似的事情

     public override void BuildDough()   { pizza.Dough   = new DoughBuilder("pan baked"); }
    

     public override void BuildDough()   { pizza.Dough   = new PanBakedDoughBuilder(); }
    

    DoughBuilder 可以转到数据库中的另一个表以正确填写 PizzaDough 类。

    【讨论】:

    • 好答案。如果我们使用静态声明的工厂类,我们就不需要 new 操作符。 Tobiask 的 builder 调用也很棒,比如 jQuery。答案是你不需要导演的地方就是我从这一切中得到的。诀窍是真正编码它。非常感谢。
    • 你对这个答案的理解越多,你就越意识到 RS Conley 知道多少。 :) 如果可以的话 +100。
    【解决方案4】:

    我会说你无法避免其中任何一个——你的部分几乎没有重载,并且在堆栈的某个地方有一个 case/if 语句。在添加新类时还必须修改代码可能是您唯一的选择。

    话虽如此,您还可以通过其他模式获得帮助 - 即可以在构建过程中帮助您的工厂。多态性的合理使用(例如,所有部分都从某种类型继承,无论是类还是接口)可以减少 ifs/cases 和重载的数量。

    希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 2010-10-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-13
      • 2016-08-25
      • 2010-12-24
      • 1970-01-01
      • 2010-12-10
      相关资源
      最近更新 更多