有哪些 常见的, 现实世界的例子 使用构建器模式?它给你买了什么?为什么不直接使用工厂模式呢?

有帮助吗?

解决方案

建筑工地和工厂恕我直言之间的主要区别,就是当你需要做很多事情来构建对象的建设者是非常有用的。例如想象一个DOM。你必须创造大量的节点和属性,让您的最终目标。工厂用于在出厂时可以很容易地创建一个方法调用中的整个对象。

使用的助洗剂的一个例子是一个建筑物的XML文档,我已经构建HTML片段例如当使用该模型可能我生成器用于构建特定类型的表中的,它可能有以下几种方法的 (参数未示出)

BuildOrderHeaderRow()
BuildLineItemSubHeaderRow()
BuildOrderRow()
BuildLineItemSubRow()

这建设者将然后吐出的HTML我。这是更容易使用比通过大量的程序方法行走来阅读。

查核维基百科上的生成器模式。

其他提示

下面是一些争论在 Java 中使用该模式和示例代码的原因,但它是四人帮所涵盖的构建器模式的实现 设计模式. 。在 Java 中使用它的原因也适用于其他编程语言。

正如约书亚·布洛赫 (Joshua Bloch) 在《 有效的 Java,第二版:

当设计其构造函数或静态工厂具有多个参数的类时,构建器模式是一个不错的选择。

我们都曾在某个时候遇到过一个带有构造函数列表的类,其中每次添加都会添加一个新的选项参数:

Pizza(int size) { ... }        
Pizza(int size, boolean cheese) { ... }    
Pizza(int size, boolean cheese, boolean pepperoni) { ... }    
Pizza(int size, boolean cheese, boolean pepperoni, boolean bacon) { ... }

这称为伸缩构造函数模式。 这种模式的问题在于,一旦构造函数有 4 或 5 个参数长,它就会变得 很难记住 所需 参数的顺序 以及在给定情况下您可能需要什么特定的构造函数。

选择 你必须使用伸缩构造函数模式 JavaBean模式 您可以在其中使用强制参数调用构造函数,然后在之后调用任何可选设置器:

Pizza pizza = new Pizza(12);
pizza.setCheese(true);
pizza.setPepperoni(true);
pizza.setBacon(true);

这里的问题是,由于对象是通过多次调用创建的,因此在构造过程中可能会处于不一致的状态。 这也需要付出很多额外的努力来保证线程安全。

更好的选择是使用构建器模式。

public class Pizza {
  private int size;
  private boolean cheese;
  private boolean pepperoni;
  private boolean bacon;

  public static class Builder {
    //required
    private final int size;

    //optional
    private boolean cheese = false;
    private boolean pepperoni = false;
    private boolean bacon = false;

    public Builder(int size) {
      this.size = size;
    }

    public Builder cheese(boolean value) {
      cheese = value;
      return this;
    }

    public Builder pepperoni(boolean value) {
      pepperoni = value;
      return this;
    }

    public Builder bacon(boolean value) {
      bacon = value;
      return this;
    }

    public Pizza build() {
      return new Pizza(this);
    }
  }

  private Pizza(Builder builder) {
    size = builder.size;
    cheese = builder.cheese;
    pepperoni = builder.pepperoni;
    bacon = builder.bacon;
  }
}

注意 Pizza 是不可变的,参数值都位于一个位置. 。因为 Builder 的 setter 方法返回 Builder 对象,它们是 能够被束缚.

Pizza pizza = new Pizza.Builder(12)
                       .cheese(true)
                       .pepperoni(true)
                       .bacon(true)
                       .build();

这使得代码易于编写并且非常易于阅读和理解。 在此示例中, 构建方法可以修改 在将参数从构建器复制到 Pizza 对象后检查参数并 如果提供了无效的参数值,则抛出 IllegalStateException。 这种模式很灵活,将来很容易向其添加更多参数。仅当构造函数的参数超过 4 或 5 个时,它才真正有用。也就是说,首先这可能是值得的 如果您怀疑将来可能会添加更多参数。

我从书中大量借用了关于这个主题的内容 有效的 Java,第二版 约书亚·布洛赫着。了解有关此模式和其他有效 Java 实践的更多信息 我强烈推荐它。

考虑的餐厅。 “今天的饭”的创建是一个工厂模式,因为你告诉厨房“让我今天的饭”,厨房(工厂)决定什么对象,生成的基础上,隐藏的标准。

如果您订购定制的比萨饼出现建设者。在这种情况下,服务员告诉厨师(制造商)“我需要一个比萨饼;!加奶酪,洋葱和腊肉给它”因此,助洗剂公开所生成的对象应具有的属性,但隐藏如何设置它们。

.NET StringBuilder类是助洗剂图案的一个很好的例子。它主要用于创建一系列步骤的字符串。你做的ToString()的最终结果始终是一个字符串,但该字符串的建立根据使用了什么样的StringBuilder类的功能而异。归纳起来,其基本思想是建立复杂的物体和隐藏它是如何正在修建的实施细节。

有关多线程问题,我们需要建立到每个线程一个复杂的对象。该对象表示正在处理的数据,并且可以根据用户输入而改变。

难道我们使用一个工厂呢?是

为什么我们没有?生成器更有意义,我猜。

工厂用于创建不同类型的有相同的基本类型(实现相同的接口或基类)的对象。

助洗剂反复建立同一类型的对象,但施工动态,因此它可以在运行时改变。

虽然经历微软的MVC框架,我有一个关于建造者模式的思想。我在为ControllerBuilder类跨越格局来了。这个类是返回控制器工厂类,然后将其用于建造混凝土控制器。

我的优势在使用生成器模式看到的是,你可以创建一个工厂自己的,并将其插入到框架中。

@Tetha,可以有由意大利人经营餐馆(框架),该供应比萨饼。为了制备意大利比萨人(对象生成器)使用欧文(厂)与比萨饼碱(碱类)。

现在印度的家伙接管来自意大利帅哥的餐厅。印度餐厅(框架)服务器DOSA而不是比萨饼。为了制备DOSA印度人(对象构建器)使用煎锅(厂)与瑞梅达(基类)

如果你看一下情况,食物是不同的,这样准备食物不同,但在同一家餐馆(同一框架下)。餐厅应建立在这样一种方式,它可以支持中国,墨西哥或任何美食。内部框架对象Builder简化到插件那种你想要的美食。例如

class RestaurantObjectBuilder
{
   IFactory _factory = new DefaultFoodFactory();

   //This can be used when you want to plugin the 
   public void SetFoodFactory(IFactory customFactory)
   {
        _factory = customFactory;
   }

   public IFactory GetFoodFactory()
   {
      return _factory;
   }
}

当你有很多的选择来处理您可以使用它。认为这样的事情JMock的:

m.expects(once())
    .method("testMethod")
    .with(eq(1), eq(2))
    .returns("someResponse");

感觉更大量的天然和...是可能的。

还有XML的建筑,建筑的字符串和许多其他的事情。试想一下,如果java.util.Map已经把作为一个建设者。你可以做这样的东西:

Map<String, Integer> m = new HashMap<String, Integer>()
    .put("a", 1)
    .put("b", 2)
    .put("c", 3);

基于之前的答案(双关语),一个很好的现实例子是 格罗维内置支持 Builders.

建设者 在里面 Groovy 文档

建设者的另一个优点是,如果你有一个工厂,还有在你代码中的一些耦合,因为作为工厂的工作,它必须知道一切都不可能创建对象 。如果添加的是可以创造另外一个对象,你将不得不修改工厂类,包括他。这发生在抽象工厂以及

在与建筑商,在另一方面,你只需要创建一个新的混凝土建造者为这个新类。导演类将保持不变,因为它接收在构造函数中的助洗剂。

此外,还有助洗剂的许多香料。神风Mercenary`s给另一个。

/// <summary>
/// Builder
/// </summary>
public interface IWebRequestBuilder
{
    IWebRequestBuilder BuildHost(string host);

    IWebRequestBuilder BuildPort(int port);

    IWebRequestBuilder BuildPath(string path);

    IWebRequestBuilder BuildQuery(string query);

    IWebRequestBuilder BuildScheme(string scheme);

    IWebRequestBuilder BuildTimeout(int timeout);

    WebRequest Build();
}

/// <summary>
/// ConcreteBuilder #1
/// </summary>
public class HttpWebRequestBuilder : IWebRequestBuilder
{
    private string _host;

    private string _path = string.Empty;

    private string _query = string.Empty;

    private string _scheme = "http";

    private int _port = 80;

    private int _timeout = -1;

    public IWebRequestBuilder BuildHost(string host)
    {
        _host = host;
        return this;
    }

    public IWebRequestBuilder BuildPort(int port)
    {
        _port = port;
        return this;
    }

    public IWebRequestBuilder BuildPath(string path)
    {
        _path = path;
        return this;
    }

    public IWebRequestBuilder BuildQuery(string query)
    {
        _query = query;
        return this;
    }

    public IWebRequestBuilder BuildScheme(string scheme)
    {
        _scheme = scheme;
        return this;
    }

    public IWebRequestBuilder BuildTimeout(int timeout)
    {
        _timeout = timeout;
        return this;
    }

    protected virtual void BeforeBuild(HttpWebRequest httpWebRequest) {
    }

    public WebRequest Build()
    {
        var uri = _scheme + "://" + _host + ":" + _port + "/" + _path + "?" + _query;

        var httpWebRequest = WebRequest.CreateHttp(uri);

        httpWebRequest.Timeout = _timeout;

        BeforeBuild(httpWebRequest);

        return httpWebRequest;
    }
}

/// <summary>
/// ConcreteBuilder #2
/// </summary>
public class ProxyHttpWebRequestBuilder : HttpWebRequestBuilder
{
    private string _proxy = null;

    public ProxyHttpWebRequestBuilder(string proxy)
    {
        _proxy = proxy;
    }

    protected override void BeforeBuild(HttpWebRequest httpWebRequest)
    {
        httpWebRequest.Proxy = new WebProxy(_proxy);
    }
}

/// <summary>
/// Director
/// </summary>
public class SearchRequest
{

    private IWebRequestBuilder _requestBuilder;

    public SearchRequest(IWebRequestBuilder requestBuilder)
    {
        _requestBuilder = requestBuilder;
    }

    public WebRequest Construct(string searchQuery)
    {
        return _requestBuilder
        .BuildHost("ajax.googleapis.com")
        .BuildPort(80)
        .BuildPath("ajax/services/search/web")
        .BuildQuery("v=1.0&q=" + HttpUtility.UrlEncode(searchQuery))
        .BuildScheme("http")
        .BuildTimeout(-1)
        .Build();
    }

    public string GetResults(string searchQuery) {
        var request = Construct(searchQuery);
        var resp = request.GetResponse();

        using (StreamReader stream = new StreamReader(resp.GetResponseStream()))
        {
            return stream.ReadToEnd();
        }
    }
}

class Program
{
    /// <summary>
    /// Inside both requests the same SearchRequest.Construct(string) method is used.
    /// But finally different HttpWebRequest objects are built.
    /// </summary>
    static void Main(string[] args)
    {
        var request1 = new SearchRequest(new HttpWebRequestBuilder());
        var results1 = request1.GetResults("IBM");
        Console.WriteLine(results1);

        var request2 = new SearchRequest(new ProxyHttpWebRequestBuilder("localhost:80"));
        var results2 = request2.GetResults("IBM");
        Console.WriteLine(results2);
    }
}

我一直不喜欢Builder模式的东西不实用,突兀,而且往往由缺乏经验的程序员滥用。它是一种模式,如果你需要从一些数据需要组装对象,它才有意义一个初始化后步(即一旦所有的数据收集 - 用它做的东西)。取而代之的是,在时间助洗剂的99%被简单地用于初始化类成员。

在这种情况下,它是好得多简单地声明类内部withXyz(...)类型setter和使它们的基准返回到自身。

考虑这样的:

public class Complex {

    private String first;
    private String second;
    private String third;

    public String getFirst(){
       return first; 
    }

    public void setFirst(String first){
       this.first=first; 
    }

    ... 

    public Complex withFirst(String first){
       this.first=first;
       return this; 
    }

    public Complex withSecond(String second){
       this.second=second;
       return this; 
    }

    public Complex withThird(String third){
       this.third=third;
       return this; 
    }

}


Complex complex = new Complex()
     .withFirst("first value")
     .withSecond("second value")
     .withThird("third value");

现在我们有一个管理自己的初始化和做几乎同样的工作作为建设者,除了它的更优雅简洁的单一类。

我以前在本土消息传递库助洗剂。库芯从线接收数据,具有生成器实例收集它,然后,一旦生成器决定it've得到它创建一个消息实例所需的一切,Builder.GetMessage()已构建使用来自所收集的数据的消息实例丝。

当我想用标准的XMLGregorianCalendar我的XML到对象的DateTime的编组在Java中,我听到了那是多么沉重的重量和使用繁琐了很多的意见。我试图comtrol的XML领域中的xs:日期时间结构来管理时区,毫秒,等等

因此,我设计了一个实用程序从一个GregorianCalendar或java.util.Date构建XMLGregorian日历。

由于我工作的地方我不能在网上分享其没有法律,但这里的客户端如何使用它的一个例子。它抽象的细节和过滤某些为XS较少使用的XMLGregorianCalendar的执行情况:日期时间

XMLGregorianCalendarBuilder builder = XMLGregorianCalendarBuilder.newInstance(jdkDate);
XMLGregorianCalendar xmlCalendar = builder.excludeMillis().excludeOffset().build();

授予该模式是多个过滤器,因为它设置在xmlCalendar为未定义字段,以便它们被排除在外,它仍然是“建立”它。我已经很容易地添加其他选项到生成器来创建一个xs:日期,和xs:在需要的时候时间struct和还操纵时区偏移

如果你曾经看到,创建和使用的XMLGregorianCalendar的代码,你会看到这是如何使人们更容易操纵。

查核InnerBuilder,一个IntelliJ IDEA的插件,增加了一个“生成器”作用于生成菜单(ALT +插入)在有效的Java描述,其生成内部生成器类

https://github.com/analytically/innerbuilder

有一个伟大的现实世界的例子是,当单元测试你的类使用。您可以使用SUT(待测系统)制造商。

示例:

类别:

public class CustomAuthenticationService
{
    private ICloudService _cloudService;
    private IDatabaseService _databaseService;

    public CustomAuthenticationService(ICloudService cloudService, IDatabaseService databaseService)
    {
        _cloudService = cloudService;
        _databaseService = databaseService;
    }

    public bool IsAuthorized(User user)
    {            
        //Implementation Details
        return true;

}

测试:

    [Test]
    public void Given_a_User_With_Permission_When_Verifying_If_Authorized_Then_Authorize_It_Returning_True()
    {
        CustomAuthenticationService sut = new CustomAuthenticationServiceBuilder();
        User userWithAuthorization = null;

        var result = sut.IsAuthorized(userWithAuthorization);

        Assert.That(result, Is.True);
    }

SUT生成器:

public class CustomAuthenticationServiceBuilder
{
    private ICloudService _cloudService;
    private IDatabaseService _databaseService;

    public CustomAuthenticationServiceBuilder()
    {
        _cloudService = new AwsService();
        _databaseService = new SqlServerService();
    }

    public CustomAuthenticationServiceBuilder WithAzureService(AzureService azureService)
    {
        _cloudService = azureService;

        return this;
    }

    public CustomAuthenticationServiceBuilder WithOracleService(OracleService oracleService)
    {
        _databaseService = oracleService;

        return this;
    }

    public CustomAuthenticationService Build()
    {
        return new CustomAuthenticationService(_cloudService, _databaseService);
    }

    public static implicit operator CustomAuthenticationService (CustomAuthenticationServiceBuilder builder)
    {
        return builder.Build();
    }
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top