Builder设计模式和Factory设计模式有什么区别?
哪一个更有利,为什么?
如果我想测试和比较/对比这些模式,如何将我的发现表示为图形?
Builder设计模式和Factory设计模式有什么区别?
哪一个更有利,为什么?
如果我想测试和比较/对比这些模式,如何将我的发现表示为图形?
Answers:
对于设计模式,通常没有适用于所有情况的“更有利”的解决方案。这取决于您需要实现的内容。
从维基百科:
- Builder致力于逐步构建复杂的对象。抽象工厂强调一系列产品对象(简单或复杂)。Builder将产品退回作为最后一步,但是就抽象工厂而言,产品将立即退回。
- 生成器通常会生成一个复合。
- 通常,设计始于使用工厂方法(不那么复杂,更可自定义的子类激增),随着设计师发现需要更多灵活性的地方而朝着抽象工厂,原型或生成器(更灵活,更复杂)发展。
- 有时,创建模式是互补的:Builder可以使用其他模式之一来实现要构建的组件。抽象工厂,构建器和原型可以在其实现中使用Singleton。
维基百科关于工厂设计模式的条目:http : //en.wikipedia.org/wiki/Factory_method_pattern
有关构建器设计模式的Wikipedia条目:http : //en.wikipedia.org/wiki/Builder_pattern
工厂只是围绕构造函数的包装函数(可能是另一个类中的包装函数)。关键区别在于,工厂方法模式要求将整个对象构建在单个方法调用中,并且所有参数都在一行中传递。最终的对象将被返回。
另一方面,构建器模式本质上是一个包装对象,它包含您可能希望传递给构造函数调用的所有可能参数。这使您可以使用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");
基本生成器示例
// 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();
比较这两个维基百科页面上的代码示例可能是值得的:
http://en.wikipedia.org/wiki/Factory_method_pattern
http://en.wikipedia.org/wiki/Builder_pattern
几乎可以将Factory模式视为Builder模式的简化版本。
在工厂模式中,工厂负责根据需要创建对象的各种子类型。
工厂方法的用户不需要知道该对象的确切子类型。工厂方法的示例createCar
可能返回一个Ford
或Honda
类型化的对象。
在Builder模式中,还可以通过builder方法创建不同的子类型,但是同一子类中对象的组成可能有所不同。
要继续汽车示例,您可能有一个createCar
builder方法,该方法创建Honda
具有4汽缸发动机的- Honda
类型的对象,或具有6汽缸的-typed的对象。构建器模式允许这种更精细的粒度。
Wikipedia上提供了Builder模式和Factory方法模式的图表。
逐步构建复杂的对象:构建器模式
通过使用一种方法创建一个简单的对象:工厂方法模式
使用多个工厂方法创建对象:抽象工厂模式
构建器模式和工厂模式看起来都非常类似于肉眼,因为它们都为您创建了对象。
这个真实的例子将使两者之间的区别更加清晰。
假设您去一家快餐店,点了Food。
比萨
辣椒,番茄,烧烤鸡肉,无菠萝
因此,不同种类的食物是通过Factory模式制作的,而特定食物的不同变体(风味)是由Builder模式制作的。
不同种类的食物
披萨,汉堡,意大利面
披萨的变体
仅奶酪,奶酪+西红柿+辣椒,奶酪+西红柿等。
两者都是创建对象的创建模式。
1)工厂模式-假设您有一个超类和N个子类。创建对象取决于传递的参数/值。
2)Builder模式-创建复杂的对象。
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.
首先,按照我的论点做一些一般的事情:
设计大型软件系统的主要挑战是它们必须灵活且不复杂地进行更改。因此,有一些指标,例如耦合和内聚。为了获得可以轻松更改或扩展其功能的系统,而无需从头开始重新设计整个系统,可以遵循设计原则(例如SOLID等)。一段时间后,一些开发人员意识到,如果他们遵循这些原则,那么有一些类似的解决方案可以很好地解决类似的问题。这些标准解决方案原来就是设计模式。
因此,设计模式将支持您遵循一般设计原则,以实现具有高凝聚力的松散耦合系统。
回答问题:
通过询问两种模式之间的差异,您必须问自己,哪种模式以哪种方式使您的系统更灵活。每个模式都有其自己的用途,可以组织系统中的类之间的依赖关系。
抽象工厂模式: GoF:“提供一个用于创建相关或相关对象族的接口,而无需指定其具体类。”
这是什么意思: 通过提供这样的接口,对每个系列产品的构造函数的调用都封装在factory类中。而且由于这是整个系统中唯一调用这些构造函数的地方,因此您可以通过实现新的工厂类来更改系统。如果通过另一工厂交换工厂的表示形式,则可以交换整套产品而无需触及大部分代码。
构建器模式: GoF:“将复杂对象的构造与其表示分离,以便相同的构造过程可以创建不同的表示。”
这是什么意思: 您将构造过程封装在另一个类中,称为Director(GoF)。该主管包含创建产品新实例的算法(例如,由其他部分组成一个复杂的产品)。为了创建整个产品的组成部分,导演使用了一个构建器。通过在导向器中交换生成器,您可以使用相同的算法来创建产品,但是可以更改单个零件的表示形式(以及产品的表示形式)。要扩展或修改产品表示形式的系统,您需要做的就是实现一个新的构建器类。
简而言之: 抽象工厂模式的目的是交换一组可以一起使用的产品。构建器模式的目的是封装创建产品的抽象算法,以将其重用于产品的不同表示形式。
在我看来,您不能说Abstract Factory Pattern是Builder Pattern的老大哥。是的,它们都是创造模式,但是这些模式的主要目的是完全不同的。
我可以看出的Builder与Factory之间的一个显着差异是以下内容
假设我们有车
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);
};
在上面的界面中,我们可以通过以下方式获得汽车:
int main()
{
BadCar = new Car(false,false,true,4,4);
}
但是,如果在创建席位时发生某些异常怎么办?您根本不会遇到任何问题,但//
假设您具有以下实现
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; }
}
现在您可以像这样创建
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();
}
在第二种情况下,即使一次操作失败,您仍然会得到Car。
可能是那辆车以后不能完美运行,但是,您会有物体。
因为Factory Method在一次调用中给您Car,而Builder却是一个接一个地构建。
虽然,这取决于要去的设计的需求。
+-------------------------------------------------------------------+---------------------------------------------------+
| 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 |
+-------------------------------------------------------------------+---------------------------------------------------+
比喻:
生成器和抽象工厂的含义不同。根据正确的用例,您必须选择合适的设计模式。
生成器的主要功能:
工厂(简单工厂)的显着特征:
通常,设计从使用Factory方法开始(不那么复杂,更易于自定义,子类激增),然后发展为Abstract Factory,Prototype或Builder。(更灵活,更复杂)
看一下相关帖子:
您可以参考以下文章以了解更多详细信息:
抽象的Factory&Builder模式都是Creation模式,但是意图不同。
抽象工厂模式强调为相关对象的族创建对象,其中:
构建器模式着重于逐步构建复杂的对象。它使表示与构造复杂对象的过程脱钩,从而可以将相同的构造过程用于不同的表示。
复杂的构造是要构造的对象由抽象表示的其他不同对象组成。
考虑一下麦当劳的菜单。菜单包含小酌,主菜和小菜。根据各个抽象的后代组成在一起,所创建的菜单具有另一种表示形式。
在那里,我们得到了具有不同表示形式的菜单的两个实例。反过来,构建过程保持不变。您创建一个包含饮料,主菜和副菜的菜单。
通过使用构建器模式,可以将创建复杂对象的算法与用于创建复杂对象的不同组件分开。
在构建器模式方面,算法封装在director中,而构建器用于创建整体部分。由于导演的其他部分也组成菜单,因此在导演算法中使用的生成器有所不同会导致不同的表示形式。菜单的创建方式保持不变。
它们之间的主要区别在于,Builder模式主要逐步描述了复杂对象的创建。在“抽象工厂”模式中,重点是对象-产品系列。Builder在最后一步返回产品。在“抽象工厂”模式下,该产品立即可用。
示例:假设我们正在创建迷宫
1.抽象工厂:
Maze* MazeGame::CreateMaze (MazeFactory& factory) {
Maze* maze = factory.MakeMaze(); /// product is available at start!!
/* Call some methods on maze */
return maze;
}
2.建造者:
Maze* MazeGame::CreateMaze (MazeBuilder& builder) {
builder.buildMaze(); /// We don't have access to maze
/* Call some methods on builder */
return builder.GetMaze();
}
我相信,当您使用相同的代码库并更改需求时,可以在一定时间内更轻松地理解/阐明Factory和Builder模式的用法以及它们之间的差异。
根据我的经验,通常从Factory模式开始,其中包括几个静态创建器方法,主要隐藏相对复杂的初始化逻辑。随着对象层次结构变得越来越复杂(或者随着添加更多类型,参数),您可能最终会在方法中填充更多参数,更不用说您将不得不重新编译Factory模块。所有这些东西,增加了创建者方法的复杂性,降低了可读性并使创建模块更加脆弱。
该点可能是过渡/扩展点。这样,您可以围绕构造参数创建包装器模块,然后可以通过添加更多抽象(可能)和实现来表示新的(相似)对象,而无需触及实际的创建逻辑。因此,您的逻辑“少了”。
坦率地说,提到某种“具有一个步骤或多个步骤创建的对象就是区别”,因为唯一的多样性因素不足以让我区分它们,因为我几乎可以面对所有情况使用两种方式现在没有任何好处。这就是我最终想到的。
与您使用工厂模式相比,构建器模式的主要优点是,如果您要创建带有许多可能的自定义项的标准对象,但是通常最终只能自定义一些。
例如,如果要编写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()
如果要为此使用工厂模式,最终将编写许多带有创建参数的所有可能组合的方法。使用构建器,您只需指定您关心的对象,然后让构建器为您构建所有其他参数。
两者非常相似,但是如果您有大量用于对象创建的参数,其中一些是可选参数,并且带有一些默认值,请使用Builder模式。
两种模式都具有相同的必要性:从某些客户端代码中隐藏复杂对象的构造逻辑。但是,是什么使“复杂的”(或有时使之复杂)的对象成为现实呢?主要是由于依赖性,或更确切地说是由于对象的状态由更局部的状态组成。您可以通过构造函数注入依赖项来设置初始对象状态,但是一个对象可能需要很多依赖项,有些对象将处于默认的初始状态(只是因为我们应该了解到将默认依赖项设置为null并不是最干净的方法),并将其他条件设置为由某些条件驱动的状态。此外,有些对象属性是某种“遗忘的依赖性”,但它们也可以采用可选状态。
有两种众所周知的方法可以控制这种复杂性:
合成/聚合:构造一个对象,构造其从属对象,然后将其连接在一起。在这里,构建者可以使确定决定构造组件规则的过程透明而灵活。
多态性:构造规则直接在子类型定义中声明,因此每个子类型都有一套规则,并且某些条件决定了这套规则中的哪一个适用于构造对象。工厂非常适合这种情况。
没有什么可以阻止将这两种方法混合使用。一个产品家族可以通过构建器来抽象对象创建,构建器可以使用工厂来确定实例化哪个组件对象。
在我看来,当您要从一堆其他对象创建对象并且零件的创建需要独立于要创建的对象时,将使用Builder模式。它有助于从客户端隐藏零件的创建,以使生成器和客户端独立。它用于创建复杂的对象(可能包含复杂属性的对象)
而工厂模式则指定您要创建一个共同家族的对象,并且希望一次将其陶瓷化。它用于更简单的对象。
建筑商和抽象工厂
在某种程度上,Builder设计模式与Abstract Factory模式非常相似。因此,重要的是要能够在使用一个或另一个时在不同情况之间做出区分。对于抽象工厂,客户端使用工厂的方法来创建自己的对象。在Builder的情况下,指示Builder类如何创建对象,然后要求它,但是将类组合在一起的方式取决于Builder类,此详细信息使这两种模式有所不同。
产品通用接口
实际上,具体构建者创建的产品具有明显不同的结构,因此,如果没有理由派生同一父类的不同产品。这也将生成器模式与抽象工厂模式区分开,抽象工厂模式创建从通用类型派生的对象。
工厂模式可让您一次创建一个对象,而构建器模式可让您中断对象的创建过程。这样,您可以在创建对象的过程中添加不同的功能。