Builder Design模式和Factory Design模式有什么区别?

Pen*_*uen 609 design-patterns factory-method factory-pattern builder-pattern

Builder设计模式和Factory设计模式有什么区别?

哪一个更有利,为什么?

如果我想测试和比较/对比这些模式,我如何将我的发现表示为图表?

Adr*_*ore 435

对于设计模式,通常没有"更有利"的解决方案适用于所有情况.这取决于您需要实施的内容.

来自维基百科:

  • Builder专注于逐步构建复杂对象.Abstract Factory强调一系列产品对象(简单或复杂).Builder将产品作为最后一步返回,但就Abstract Factory而言,产品会立即返回.
  • Builder经常构建一个Composite.
  • 通常,设计开始使用工厂方法(不太复杂,可定制,子类增加),并逐渐向抽象工厂,原型或构建器(更灵活,更复杂)发展,因为设计师发现需要更多灵活性的地方.
  • 有时创建模式是互补的:Builder可以使用其他模式之一来实现构建哪些组件.Abstract Factory,Builder和Prototype可以在其实现中使用Singleton.

工厂设计模式的维基百科条目:http: //en.wikipedia.org/wiki/Factory_method_pattern

建筑师设计模式的维基百科条目:http: //en.wikipedia.org/wiki/Builder_pattern

  • 这正是区别.只有在一步无法生成对象时才需要Builder.一个很好的例子就是复杂对象的反序列化过程.通常,必须逐个检索复杂对象的参数. (143认同)
  • @Joel:我同意某些模式比其他模式更常见(例如,Factory似乎比Builder更常见),但我的意思是,无论方案如何,它们都不会总是优于其他模式. (4认同)
  • 很好的答案,尽管有两件事值得补充:1)Builder 主要用于使用 Fluent API 构建 POJO(例如 Person.builder().withName("Sam").withAge(38).build()。2)根据我的经验,构建器对于域对象的 POJO 创建很有用,而工厂对于创建像 PdfGeneratorFactory 类这样的服务对象很有用。服务对象可以在工厂中缓存以供多次使用,而构建器总是按设计创建一个新对象。 (3认同)
  • 这个答案所做的只是重复维基百科。它不再增加任何东西。需要自己的话来解释差异是什么。 (2认同)

Jam*_*gan 318

工厂只是构造函数(可能是不同类中的一个)的包装函数.关键区别在于工厂方法模式要求在单个方法调用中构建整个对象,所有参数都在一行中传递.将返回最终对象.

另一方面,构建器模式实质上是围绕可能要传递给构造函数调用的所有可能参数的包装器对象.这允许您使用setter方法慢慢构建参数列表.构建器类的另一个方法是build()方法,它只是将构建器对象传递给所需的构造函数,并返回结果.

在Java这样的静态语言中,当您拥有多个(可能是可选的)参数时,这变得更加重要,因为它避免了对所有可能的参数组合使用伸缩构造函数的要求.此外,构建器允许您使用setter方法来定义在调用构造函数后无法直接修改的只读或私有字段.

基本工厂示例

// Factory
static class FruitFactory {
    static Fruit create(name, color, firmness) {
        // Additional logic
        return new Fruit(name, color, firmness);
    }
}

// Usage
Fruit fruit = FruitFactory.create("apple", "red", "crunchy");
Run Code Online (Sandbox Code Playgroud)

基本构建器示例

// Builder
class FruitBuilder {
    String name, color, firmness;
    FruitBuilder setName(name)         { this.name     = name;     return this; }
    FruitBuilder setColor(color)       { this.color    = color;    return this; }
    FruitBuilder setFirmness(firmness) { this.firmness = firmness; return this; }
    Fruit build() {
        return new Fruit(this); // Pass in the builder
    }
}

// Usage
Fruit fruit = new FruitBuilder()
        .setName("apple")
        .setColor("red")
        .setFirmness("crunchy")
        .build();
Run Code Online (Sandbox Code Playgroud)

可能值得比较来自这两个维基百科页面的代码示例:

http://en.wikipedia.org/wiki/Factory_method_pattern
http://en.wikipedia.org/wiki/Builder_pattern

  • 有两种不同的模式通常被称为“构建器”: **a)** 构建器,根据 Erich **Gamma**、Richard Helm、Ralph Johnson 和 John 所著的《设计模式:可重用面向对象软件的元素》 Vlissides --- **b)** 根据 Joshua **Bloch** 的“Effective Java”构建器。“当面对许多构造函数参数时,请考虑使用构建器。” 此模式使用[方法级联](https://en.wikipedia.org/wiki/Method_cascading)/[流畅的接口](https://martinfowler.com/bliki/FluentInterface.html)来初始化(不可变的)对象很多属性。 (4认同)
  • 这不是构建器模式 imo 的正确用法,即使在您传递的 wiki 链接中,用法也不同。这个 FruitBuilder 是 Director 和 Builder 组件的一些组合,您可以在其中调用应该属于 Director 的 build() 和属于 Builder 组件的 setter。Director 应该包含关于如何使用 Builders 方法创建对象的业务逻辑。Fluent apis 不是构建器模式,StringBuilder 也不是构建器模式。 (2认同)

Ben*_*n S 272

Factory模式几乎可以看作是Builder模式的简化版本.

工厂模式中,工厂负责根据需要创建对象的各种子类型.

工厂方法的用户不需要知道该对象的确切子类型.工厂方法的示例createCar可能返回一个Ford或一个Honda类型化的对象.

Builder模式中,构造方法也会创建不同的子类型,但对象的组成可能在同一个子类中有所不同.

要继续汽车示例,您可能有一个createCar构建器方法,该方法Honda使用4柱面引擎或Honda具有6个柱面的对象创建对象.构建器模式允许更精细的粒度.

维基百科上提供了Builder模式Factory方法模式的图表.

  • 构建器模式就像放大对象的构造一样.大对象由其他对象组成,它在递归上进一步组成.虽然工厂只会在一个电话中找到你的东西.这种理解是否正确? (10认同)

Joe*_*orn 58

构建器设计模式描述了一个对象,该对象知道如何通过几个步骤制作特定类型的另一个对象.它在每个中间步骤保持目标项的所需状态.想想StringBuilder通过什么来生成最终字符串.

工厂设计模式描述了一个对象,该对象知道如何在一个步骤中创建多个不同但相关的对象,其中基于给定参数选择特定类型.想想序列化系统,您可以在其中创建序列化程序,并在一次加载调用中构建所需的对象.

  • 只是一些提示:构建器模式的好例子是"流畅的接口",ADO.NET充满了"工厂"和"抽象工厂"实现(即DbFactory). (7认同)

小智 46

  • 逐步构造复杂对象:构建器模式

  • 通过使用单个方法创建一个简单对象:工厂方法模式

  • 使用多种工厂方法创建对象:抽象工厂模式


Ton*_*ony 16

两者都是Creational模式,用于创建Object.

1)工厂模式 - 假设您有一个超类和N个子类.创建对象取决于传递的参数/值.

2)构建器模式 - 创建复杂对象.

Ex: Make a Loan Object. Loan could be house loan, car loan ,
    education loan ..etc. Each loan will have different interest rate, amount ,  
    duration ...etc. Finally a complex object created through step by step process.
Run Code Online (Sandbox Code Playgroud)


Roh*_*ngh 16

生成器模式和工厂模式,看起来都非常类似于肉眼,因为它们都为您创建对象.

但你需要仔细观察

这个现实生活中的例子将使两者之间的区别更加清晰.

假设,你去了一家快餐店订购了食品.

1)什么食物?

比萨

2)什么浇头?

辣椒,番茄,烧烤鸡,没有菠萝

因此,不同种类的食物是由工厂模式制作的,但特定食物的不同变体(风味)是由Builder模式制作的.

不同种类的食物

比萨饼,汉堡,意大利面

比萨的变种

只有奶酪,奶酪+番茄+辣椒,奶酪+番茄等.

代码示例

您可以在此处查看
Builder Pattern Factory Pattern的两种模式的示例代码实现

  • 感谢提供示例代码!您的示例很好地区分了这两种模式。 (2认同)

Jan*_*nis 12

首先是一些关于我的论证的一般性事项:

设计大型软件系统的主要挑战是它们必须灵活且不易变化.出于这个原因,有一些指标,如耦合和内聚.要实现易于更改或扩展其功能而无需从头开始重新设计整个系统的系统,您可以遵循设计原则(如SOLID等).一段时间后,一些开发人员认识到,如果他们遵循这些原则,那么有一些类似的解决方 这些标准解决方案被证明是设计模式.

因此,设计模式是为了支持您遵循一般设计原则,以实现具有高内聚力的松散耦合系统.

回答这个问题:

通过询问两种模式之间的区别,您必须问自己哪种模式可以使您的系统更灵活.每个模式都有自己的目的,即组织系统中各类之间的依赖关系.

抽象工厂模式: GoF:"提供一个接口,用于创建相关或依赖对象的族,而无需指定其具体类."

这意味着什么: 通过提供这样的接口,对每个系列产品的构造函数的调用被封装在工厂类中.因为这是整个系统中唯一一个调用这些构造函数的地方,你可以通过实现一个新的工厂类来改变你的系统.如果您通过另一个交换工厂的代表,您可以交换一整套产品而无需接触大部分代码.

构建器模式: GoF:"将复杂对象的构造与其表示分开,以便相同的构造过程可以创建不同的表示."

这意味着什么: 你将构造过程封装在另一个类中,称为导演(GoF).该导演包含创建产品新实例的算法(例如,从其他部分组成复杂的产品).为了创建整个产品的组成部分,导演使用了一个建筑师.通过在导向器中交换构建器,您可以使用相同的算法来创建产品,但更改单个部件的表示(以及产品的表示).要在产品表示中扩展或修改系统,您需要做的就是实现一个新的构建器类.

简而言之: 抽象工厂模式的目的是交换一组可以一起使用的产品.Builder Pattern的目的是封装创建产品的抽象算法,以便将其重用于产品的不同表示.

在我看来,你不能说抽象工厂模式是Builder模式的大哥.是的,它们都是创作模式,但模式的主要意图完全不同.


Foo*_*ooo 7

建造者和工厂之间的一个显着区别我可以得出如下

假设我们有车

class Car
{
  bool HasGPS;
  bool IsCityCar;
  bool IsSportsCar;
  int   Cylenders;
  int Seats;

  public:
     void Car(bool hasGPs=false,bool IsCityCar=false,bool IsSportsCar=false, int Cylender=2, int Seats=4);
 };
Run Code Online (Sandbox Code Playgroud)

在上面的界面中我们可以通过以下方式获得汽车:

 int main()
 {
    BadCar = new Car(false,false,true,4,4);
  }
Run Code Online (Sandbox Code Playgroud)

但是如果在创建座位时发生了一些异常呢?你不会全部获得对象//但是

假设你有如下的实现

class Car
 {
    bool mHasGPS;
    bool mIsCityCar;
    bool mIsSportsCar;
    int mCylenders;
    int mSeats;

 public:
    void Car() : mHasGPs(false), mIsCityCar(false), mIsSportsCar(false), mCylender(2), mSeats(4) {}
    void SetGPS(bool hasGPs=false)  {mHasGPs = hasGPs;}
    void SetCity(bool CityCar)  {mIsCityCar = CityCar;}
    void SetSports(bool SportsCar)  {mIsSportsCar = SportsCar;}
    void SetCylender(int Cylender)  {mCylenders = Cylender;}    
    void SetSeats(int seat) {mSeats = seat;}    
};

 class CarBuilder 
 {
    Car* mCar;
public:
        CarBuilder():mCar(NULL) {   mCar* = new Car();  }
        ~CarBuilder()   {   if(mCar)    {   delete mCar;    }
        Car* GetCar()   {   return mCar; mCar=new Car();    }
        CarBuilder* SetSeats(int n) {   mCar->SetSeats(n); return this; }
        CarBuilder* SetCylender(int n)  {   mCar->SetCylender(n); return this;  }
        CarBuilder* SetSports(bool val) {   mCar->SetSports(val); return this;  }
        CarBuilder* SetCity(bool val)   {   mCar->SetCity(val); return this;    }
        CarBuilder* SetGPS(bool val)    {   mCar->SetGPS(val); return this; }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以像这样创建

 int main()
 {
   CarBuilder* bp =new CarBuilder;
    Car* NewCar  = bp->SetSeats(4)->SetSports(4)->SetCity(ture)->SetGPS(false)->SetSports(true)->GetCar();

     bp->SetSeats(2);

     bp->SetSports(4);

     bp->SetCity(ture);

     bp->SetSports(true)

     Car* Car_II=  bp->GetCar();

  }
Run Code Online (Sandbox Code Playgroud)

在第二种情况下,即使一次操作失败,您仍然可以获得汽车.

可能是那辆车以后不能很好地工作但是,你会有这个对象.

因为Factory Method会在单次调用中为您提供Car,而Builder会逐个构建.

虽然,这取决于设计的需要.

  • 毫无疑问,根本没有车比有一辆废车要好——如果你只是在使用休息时才发现问题怎么办? (2认同)
  • 我想澄清一点,我实际上是建造者模式的忠实粉丝,但是,不是因为你给出的原因.无效的对象应该在构造时失败,进程越远,发现的bug就越贵.使用构建器模式,如果缺少任何所需的数据,则构建方法(在您的示例中称为getCar())抛出异常是正常的. (2认同)

Pre*_*raj 7

+-------------------------------------------------------------------+---------------------------------------------------+
|                              Builder                              |                      Factory                      |
+-------------------------------------------------------------------+---------------------------------------------------+
| Return only single instance to handle complex object construction | Retrun various instances on multiple constructors |
| No interface required                                             | Interface driven                                  |
| Inner classes is involved (to avoid telescopic constructors)      | Subclasses are involved                           |
+-------------------------------------------------------------------+---------------------------------------------------+  
Run Code Online (Sandbox Code Playgroud)

伸缩构造函数模式

比喻:

  • 工厂:考虑一家餐厅。“今天的饭”的创建是一种工厂模式,因为您告诉厨房“给我今天的饭”,而厨房(工厂)根据隐藏的标准决定要生成的对象。
  • 生成器:如果您订购自定义披萨,则会显示生成器。在这种情况下,服务员告诉厨师(建造者):“我需要一个披萨;在其中添加奶酪,洋葱和培根!” 因此,构建器公开了所生成对象应具有的属性,但隐藏了如何设置它们。

礼貌


Ham*_* C. 7

工厂:用于创建对象的实例,其中对象的依赖项完全由工厂持有。对于抽象工厂模式,同一个抽象工厂往往有很多具体的实现。工厂的正确实现是通过依赖注入注入的。

Builder : 用于构建不可变对象,当要实例化的对象的依赖部分预先已知,部分由构建器的客户端提供时。


Rav*_*abu 5

生成器抽象工厂的含义不同。根据正确的用例,您必须选择合适的设计模式。

生成器的主要功能:

  1. 构建器模式使用简单对象并使用逐步方法来构建复杂对象
  2. Builder类逐步构建最终对象。该构建器独立于其他对象
  3. 在这种情况下替换为Factory方法/ Abstract Factory:过多的参数无法从客户端程序传递到Factory类,这很容易出错
  4. 某些参数可能是可选的,这与在Factory中强制发送所有参数不同

工厂(简单工厂)的主要特点:

  1. 创作模式
  2. 基于继承
  3. 工厂返回一个工厂方法(接口),该方法又返回具体对象
  4. 您可以将新的具体对象替换为接口,并且客户端(调用方)不应了解所有具体实现
  5. 客户端始终仅访问界面,并且可以在Factory方法中隐藏对象创建详细信息。

通常,设计从使用Factory方法开始(不那么复杂,更可定制,子类激增),然后演变为Abstract FactoryPrototypeBuilder(更灵活,更复杂)

看一下相关帖子:

将构建器放在单独的类中(流利的界面)

设计模式:工厂vs工厂方法vs抽象工厂

您可以参考以下文章以了解更多详细信息:

来源制作

journaldev


dan*_*ela 5

与工厂模式相比,构建器模式的主要优点是,如果您想要创建一些具有大量可能的自定义项的标准对象,但您通常最终只会自定义其中一些。

例如,如果您想编写一个 HTTP 客户端 - 您将设置一些默认参数,例如默认写入/读取超时、协议、缓存、DNS、拦截器等。

您的客户端的大多数用户只会使用这些默认参数,而其他一些用户可能想要自定义一些其他参数。在某些情况下,您只想更改超时并按原样使用其余部分,而在其他情况下,您可能需要自定义例如缓存。

以下是实例化客户端的可能方法(取自 OkHttpClient):

//just give me the default stuff
HttpClient.Builder().build()   

//I want to use custom cache
HttpClient.Builder().cache(MyCache()).build() 

//I want custom connection timeout
HttpClient.Builder().connectTimeout(30, TimeUnit.SECONDS).build() 

//I am more interested in read/write timeout
HttpClient.Builder()
        .readTimeout(30, TimeUnit.SECONDS)
        .writeTimeout(30, TimeUnit.SECONDS).build()
Run Code Online (Sandbox Code Playgroud)

如果您为此使用工厂模式,您最终将编写大量具有所有可能的创建参数组合的方法。使用构建器,您只需指定您关心的参数,然后让构建器为您构建它,并处理所有其他参数。