Java 你什么时候会使用建造者模式?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/328496/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 13:14:31  来源:igfitidea点击:

When would you use the Builder Pattern?

javadesign-patternsbuilder

提问by Charles Graham

What are some common, real world examplesof using the Builder Pattern? What does it buy you? Why not just use a Factory Pattern?

使用 Builder Pattern 的一些常见的真实的例子是什么?它给你买什么?为什么不直接使用工厂模式?

采纳答案by JoshBerke

The key difference between a builder and factory IMHO, is that a builder is useful when you need to do lots of things to build an object. For example imagine a DOM. You have to create plenty of nodes and attributes to get your final object. A factory is used when the factory can easily create the entire object within one method call.

构建器和工厂恕我直言之间的主要区别在于,当您需要做很多事情来构建对象时,构建器很有用。例如,想象一个 DOM。您必须创建大量节点和属性才能获得最终对象。当工厂可以在一个方法调用中轻松创建整个对象时,将使用工厂。

One example of using a builder is a building an XML document, I've used this model when building HTML fragments for example I might have a Builder for building a specific type of table and it might have the following methods (parameters are not shown):

使用构建器的一个例子是构建一个 XML 文档,我在构建 HTML 片段时使用了这个模型,例如我可能有一个构建器来构建特定类型的表,它可能具有以下方法(未显示参数)

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

This builder would then spit out the HTML for me. This is much easier to read than walking through a large procedural method.

然后这个构建器会为我吐出 HTML。这比遍历大型程序方法更容易阅读。

Check out Builder Pattern on Wikipedia.

查看Wikipedia 上的 Builder Pattern

回答by Dustin

You use it when you have lots of options to deal with. Think about things like jmock:

当您有很多选择要处理时,您可以使用它。想想像 jmock 这样的东西:

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

It feels a lot more natural and is...possible.

感觉更自然,而且……有可能。

There's also xml building, string building and many other things. Imagine if java.util.Maphad put as a builder. You could do stuff like this:

还有 xml 构建、字符串构建和许多其他东西。想象一下,如果java.util.Map把它当作一个建设者。你可以做这样的事情:

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

回答by Cameron MacFarland

For a multi-threaded problem, we needed a complex object to be built up for each thread. The object represented the data being processed, and could change depending on the user input.

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

Could we use a factory instead? Yes

我们可以用工厂代替吗?是的

Why didn't we? Builder makes more sense I guess.

为什么我们没有?我想 Builder 更有意义。

Factories are used for creating different types of objects that are the same basic type (implement the same interface or base class).

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

Builders build the same type of object over and over, but the construction is dynamic so it can be changed at runtime.

构建器一遍又一遍地构建相同类型的对象,但构建是动态的,因此可以在运行时更改。

回答by Ken Gentle

Building on the previous answers (pun intended), an excellent real-world example is Groovy's built in support for Builders.

基于之前的答案(双关语),一个极好的现实世界示例是GroovyBuilders.

See Buildersin the Groovy Documentation

建设者Groovy的文档

回答by wasker

I used builder in home-grown messaging library. The library core was receiving data from the wire, collecting it with Builder instance, then, once Builder decided it've got everything it needed to create a Message instance, Builder.GetMessage() was constructing a message instance using the data collected from the wire.

我在自己开发的消息传递库中使用了构建器。库核心从线路接收数据,用 Builder 实例收集它,然后,一旦 Builder 决定它已经拥有创建 Message 实例所需的一切,Builder.GetMessage() 就会使用从金属丝。

回答by Tetha

Consider a restaurant. The creation of "today's meal" is a factory pattern, because you tell the kitchen "get me today's meal" and the kitchen (factory) decides what object to generate, based on hidden criteria.

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

The builder appears if you order a custom pizza. In this case, the waiter tells the chef (builder) "I need a pizza; add cheese, onions and bacon to it!" Thus, the builder exposes the attributes the generated object should have, but hides how to set them.

如果您订购定制的比萨饼,构建器就会出现。在这种情况下,服务员告诉厨师(建造者)“我需要一个披萨;往里面加奶酪、洋葱和培根!” 因此,构建器公开了生成的对象应该具有的属性,但隐藏了如何设置它们。

回答by Tetha

.NET StringBuilder class is a great example of builder pattern. It is mostly used to create a string in a series of steps. The final result you get on doing ToString() is always a string but the creation of that string varies according to what functions in the StringBuilder class were used. To sum up, the basic idea is to build complex objects and hide the implementation details of how it is being built.

.NET StringBuilder 类是构建器模式的一个很好的例子。它主要用于通过一系列步骤创建字符串。执行 ToString() 的最终结果始终是一个字符串,但该字符串的创建根据使用的 StringBuilder 类中的函数而有所不同。总而言之,基本思想是构建复杂对象并隐藏其构建方式的实现细节。

回答by Aaron

Below are some reasons arguing for the use of the pattern and example code in Java, but it is an implementation of the Builder Pattern covered by the Gang of Four in Design Patterns. The reasons you would use it in Java are also applicable to other programming languages as well.

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

As Joshua Bloch states in Effective Java, 2nd Edition:

正如 Joshua Bloch 在Effective Java,第 2 版中所述

The builder pattern is a good choice when designing classes whose constructors or static factories would have more than a handful of parameters.

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

We've all at some point encountered a class with a list of constructors where each addition adds a new option parameter:

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

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

This is called the Telescoping Constructor Pattern.The problem with this pattern is that once constructors are 4 or 5 parameters long it becomes difficult to rememberthe required order of the parametersas well as what particular constructor you might want in a given situation.

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

One alternativeyou have to the Telescoping Constructor Pattern is the JavaBean Patternwhere you call a constructor with the mandatory parameters and then call any optional setters after:

伸缩构造器模式的一种选择JavaBean 模式,您可以在其中调用具有强制参数的构造函数,然后在以下之后调用任何可选的 setter:

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

The problem here is that because the object is created over several calls it may be in an inconsistent state partway through its construction.This also requires a lot of extra effort to ensure thread safety.

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

The better alternative is to use the Builder Pattern.

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

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;
  }
}

Note that Pizza is immutable and that parameter values are all in a single location. Because the Builder's setter methods return the Builder object they are able to be chained.

请注意Pizza 是不可变的,并且参数值都在一个位置。因为 Builder 的 setter 方法返回 Builder 对象,所以它们能够被链接

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

This results in code that is easy to write and very easy to read and understand.In this example, the build method could be modifiedto check parameters after they have been copied from the builder to the Pizza object and throw an IllegalStateException if an invalid parameter value has been supplied.This pattern is flexible and it is easy to add more parameters to it in the future. It is really only useful if you are going to have more than 4 or 5 parameters for a constructor. That said, it might be worthwhile in the first place if you suspect you may be adding more parameters in the future.

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

I have borrowed heavily on this topic from the book Effective Java, 2nd Editionby Joshua Bloch. To learn more about this pattern and other effective Java practices I highly recommend it.

我从Joshua Bloch 的Effective Java 第二版一书中大量借用了这个主题。要了解有关此模式和其他有效 Java 实践的更多信息,我强烈推荐它。

回答by Lino Rosa

Another advantage of the builder is that if you have a Factory, there is still some coupling in you code, because for the Factory to work, it has to know all the objects it can possibly create. If you add another object that could be created, you will have to modify the factory class to include him. This happens in the Abstract Factory as well.

构建器的另一个优点是,如果你有一个工厂,你的代码中仍然存在一些耦合,因为工厂要工作,它必须知道它可能创建的所有对象。如果添加另一个可以创建的对象,则必须修改工厂类以包含他。这也发生在抽象工厂中。

With the builder, on the other hand, you just have to create a new concrete builder for this new class. The director class will stay the same, because it receives the builder in the constructor.

另一方面,使用构建器,您只需为这个新类创建一个新的具体构建器。导演类将保持不变,因为它在构造函数中接收构建器。

Also, there are many flavors of builder. Kamikaze Mercenary`s gives another one.

此外,构建器有多种口味。Kamikaze Mercenary's 给了另一个。

回答by Nitin

While going through Microsoft MVC framework, I got a thought about builder pattern. I came across the pattern in the ControllerBuilder class. This class is to return the controller factory class, which is then used to build concrete controller.

在浏览 Microsoft MVC 框架时,我想到了构建器模式。我在 ControllerBuilder 类中遇到了这种模式。这个类是返回控制器工厂类,然后用于构建具体的控制器。

Advantage I see in using builder pattern is that, you can create a factory of your own and plug it into the framework.

我在使用构建器模式中看到的优点是,您可以创建自己的工厂并将其插入框架。

@Tetha, there can be a restaurant (Framework) run by Italian guy, that serves Pizza. In order to prepare pizza Italian guy (Object Builder) uses Owen (Factory) with a pizza base (base class).

@Tetha,可以有一家由意大利人经营的餐厅(Framework),供应比萨。为了准备比萨,意大利人 (Object Builder) 使用 Owen (Factory) 和比萨基础(基类)。

Now Indian guy takes over the restaurant from Italian guy. Indian restaurant (Framework) servers dosa instead of pizza. In order to prepare dosa Indian guy (object builder) uses Frying Pan (Factory) with a Maida (base class)

现在印度人接管了意大利人的餐厅。印度餐厅 (Framework) 提供 dosa 而不是披萨。为了准备 dosa 印度人(对象生成器)使用带有 Maida(基类)的煎锅(工厂)

If you look at scenario, food is different,way food is prepared is different, but in the same restaurant (under same framework). Restaurant should be build in such a way that it can support Chinese, Mexican or any cuisine. Object builder inside framework facilitates to plugin kind of cuisine you want. for example

如果你看场景,食物不同,食物准备的方式不同,但在同一家餐厅(在同一框架下)。餐厅的建造方式应能支持中餐、墨西哥菜或任何美食。框架内的对象构建器有助于插入您想要的美食。例如

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;
   }
}