Builder设计模式和Factory设计模式之间的区别是什么?
哪一种更有利?为什么?
如果我想测试和比较/对比这些模式,我如何将我的发现表示为图表?
Builder设计模式和Factory设计模式之间的区别是什么?
哪一种更有利?为什么?
如果我想测试和比较/对比这些模式,我如何将我的发现表示为图表?
构建器设计模式描述了一个对象,该对象知道如何在几个步骤中创建另一个特定类型的对象。它在每个中间步骤保持目标项所需的状态。想想StringBuilder是如何生成最终字符串的。
工厂设计模式描述了一个对象,该对象知道如何在一个步骤中创建几种不同但相关的对象,其中特定类型是基于给定参数选择的。想想串行化系统,在这里创建串行化器,它在一次加载调用中构造所需的in对象。
Factory模式几乎可以看作是Builder模式的简化版本。
在Factory模式中,工厂负责根据需要创建对象的各种子类型。
工厂方法的用户不需要知道该对象的确切子类型。工厂方法createCar的示例可能返回Ford或Honda类型的对象。
在生成器模式中,不同的子类型也由生成器方法创建,但同一子类中对象的组成可能不同。
要继续汽车示例,您可能需要一个createCarbuilder方法,该方法创建一个带有4缸发动机的Honda类型的对象,或者一个带有6缸的Honda型对象。构建器模式允许这种更精细的粒度。
生成器模式和工厂方法模式的图表都可以在维基百科上找到。
对于设计模式,通常没有适用于所有情况的“更有利”解决方案。这取决于您需要实施什么。
来自维基百科:
Builder专注于构建一步一步复杂的物体。摘要工厂强调产品系列对象(简单或复杂)。生成器将产品作为最终产品返回步骤,但就抽象而言工厂关心,产品得到立即返回。生成器通常构建一个复合。通常,设计开始时使用工厂方法(不那么复杂,更多可定制,子类激增)并向抽象工厂发展,原型或生成器(更灵活,更复杂)发现更灵活的地方需要。有时创造模式是互补的:构建者可以使用要实现的其他模式构建哪些组件。摘要工厂、建造商和原型可以使用Singleton实现。
工厂设计模式维基百科条目:http://en.wikipedia.org/wiki/Factory_method_pattern
构建器设计模式的维基百科条目:http://en.wikipedia.org/wiki/Builder_pattern
两者都是创造模式,以创建对象。
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.
工厂只是一个围绕构造函数(可能是不同类中的一个)的包装函数。关键区别在于,工厂方法模式要求在单个方法调用中构建整个对象,所有参数都在一行中传递。将返回最终对象。
另一方面,构建器模式本质上是一个包装器对象,它围绕着您可能希望传递到构造函数调用中的所有可能参数。这允许您使用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_patternhttp://en.wikipedia.org/wiki/Builder_pattern
生成器和抽象工厂生成器设计模式在某种程度上与抽象工厂模式非常相似。这就是为什么在使用其中一种或另一种情况时,能够区分不同的情况是很重要的。在抽象工厂的情况下,客户端使用工厂的方法来创建自己的对象。在Builder的例子中,Builder类被指示如何创建对象,然后被要求创建对象,但是类的组合方式取决于Builder类,这一细节决定了两种模式之间的区别。产品通用接口在实践中,由混凝土建设者创建的产品具有明显不同的结构,因此如果没有理由派生不同的产品,则生成一个公共的父类。这也将生成器模式与抽象工厂模式区分开来,抽象工厂模式创建从公共类型派生的对象。
发件人:http://www.oodesign.com/builder-pattern.html
区别很明显在生成器模式中,生成器将为您创建特定类型的对象。你必须告诉我什么建筑商必须建造。在工厂模式中,使用抽象类可以直接构建特定对象。
在这里,生成器类充当主类和特定类型类之间的中介。更抽象。
在我看来当您希望从一堆其他对象创建对象,并且创建零件需要独立于要创建的对象时,使用生成器模式。它有助于向客户端隐藏部件的创建,以使构建器和客户端独立。它用于创建复杂对象(可能包含复杂财产的对象)
而工厂模式指定您要创建一个公共族的对象,并希望它立即被切去。它用于更简单的对象。
我可以看出建筑商和工厂之间的一个显著区别是
假设我们有一辆车
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);
}
但是如果在创建Seats时发生了一些异常呢???你根本得不到这个物体//但是
假设您有如下实现
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();
}
在第二种情况下,即使一次操作失败,你仍然可以得到汽车。
可能是这辆车后来不太好用了,但你会有目标的。
因为Factory方法在一次调用中为您提供Car,而Builder则逐个构建。
尽管如此,这取决于哪一位的需要。
抽象工厂和生成器模式都是创造性模式,但目的不同。
抽象工厂模式强调为相关对象族创建对象,其中:
每个族都是从公共基类/接口派生的一组类。每个对象都会作为一次调用的结果立即返回。
构建器模式侧重于一步一步地构建一个复杂的对象。它将表示与构造复杂对象的过程解耦,以便相同的构造过程可以用于不同的表示。
Builder对象封装了复杂对象的配置。Director对象知道使用Builder的协议,其中协议定义了构建复杂对象所需的所有逻辑步骤。
生成器和抽象工厂有着不同的目的。根据正确的用例,您必须选择合适的设计模式。
生成器的显著特点:
生成器模式使用简单对象和分步方法构建复杂对象生成器类逐步构建最终对象。此生成器独立于其他对象在这种情况下替换为Factory方法/抽象工厂:从客户端程序传递给Factory类的参数太多,容易出错某些参数可能是可选的,不像工厂中强制发送所有参数
工厂(简单工厂)的显著特点:
创建型模式基于继承Factory返回一个Factory方法(接口),然后返回具体对象您可以用新的具体对象替换接口,客户端(调用者)不应该知道所有具体实现客户端始终只访问接口,您可以在Factory方法中隐藏对象创建详细信息。
通常,设计从使用工厂方法(不那么复杂,更可定制,子类激增)开始,并向抽象工厂、原型或生成器(更灵活,更复杂)发展
查看相关帖子:
将生成器保持在单独的类中(流畅的接口)
设计模式:工厂vs工厂方法vs抽象工厂
有关详细信息,请参阅以下文章:
资源制造
日志记录设备
工厂模式在运行时创建一个类的具体实现,即它的主要目的是使用多态性来允许子类决定实例化哪个类。这意味着在编译时我们不知道将要创建的确切类,而Builder模式主要涉及解决伸缩构造函数反模式的问题,这是由于类的大量可选字段而产生的。在构建器模式中,没有多态性的概念,因为我们知道在编译时要构造什么对象。
这两种模式的唯一共同主题是在工厂方法和构建方法后面隐藏构造函数和对象创建,以改进对象构造。
我相信,当您处理相同的代码库和不断变化的需求时,在一定的时间段内,Factory&Builder模式的用法和它们之间的区别可以更容易理解/澄清。
根据我的经验,通常从Factory模式开始,包括两个静态创建者方法,主要隐藏相对复杂的初始化逻辑。随着对象层次结构变得越来越复杂(或添加更多类型、参数),您可能最终会使用更多参数填充方法,更不用说您必须重新编译Factory模块。所有这些都会增加创建者方法的复杂性,降低可读性,并使创建模块更加脆弱。
该点可能是过渡/延伸点。通过这样做,您可以围绕构造参数创建一个包装器模块,然后您可以通过添加更多抽象(可能)和实现来表示新的(类似的)对象,而无需接触实际的创建逻辑。所以你有了“不那么复杂”的逻辑。
坦率地说,提到某种“一步或多步创建一个对象是不同的”,因为唯一的多样性因素不足以让我区分它们,因为我可以对我目前面临的几乎所有情况都使用这两种方法,而没有任何益处。这就是我最后想到的。
这两种模式都有相同的必要性:对某些客户端代码隐藏复杂对象的构造逻辑。但是,是什么使一个对象变得“复杂”(或有时变得复杂)?主要是由于依赖关系,或者更确切地说是由更多部分状态组成的对象的状态。您可以通过构造函数注入依赖项来设置初始对象状态,但一个对象可能需要很多依赖项,其中一些依赖项将处于默认初始状态(只是因为我们应该了解到,将默认依赖项设置为null不是最干净的方式),而另一些依赖项则设置为由某种条件驱动的状态。此外,有些对象财产是某种“不经意的依赖关系”,但它们也可以假定为可选状态。
有两种众所周知的方法来控制这种复杂性:
组合/聚合:构造一个对象,构造其依赖对象,然后连接在一起。在这里,构建器可以使确定引导组件构建的规则的过程透明而灵活。多态性:构造规则直接声明到子类型定义中,因此每个子类型都有一组规则,某些条件决定了这些规则中的哪一个适用于构造对象。工厂完全适合这种情况。
没有什么可以阻止这两种方法的混合。一个产品系列可以抽象对象创建,由生成器完成,生成器可以使用工厂来确定实例化哪个组件对象。
首先,要遵循我的论证:
设计大型软件系统的主要挑战是它们必须灵活且不复杂地进行更改。出于这个原因,有一些度量,如耦合和内聚。为了实现可以轻松更改或扩展其功能而无需从头设计整个系统的系统,您可以遵循设计原则(如SOLID等)。过了一段时间,一些开发人员意识到,如果他们遵循这些原则,就会有一些类似的解决方案可以很好地解决类似问题。这些标准解决方案被证明是设计模式。
因此,设计模式支持您遵循一般设计原则,以实现具有高内聚性的松散耦合系统。
回答问题:
通过询问两种模式之间的差异,你必须问问自己,哪种模式使你的系统更灵活。每个模式都有自己的目的来组织系统中类之间的依赖关系。
抽象工厂模式:GoF:“提供一个接口来创建相关或依赖对象的族,而不指定它们的具体类。”
这意味着什么:通过提供这样的接口,对每个系列产品的构造函数的调用被封装在工厂类中。因为这是整个系统中唯一调用这些构造函数的地方,所以可以通过实现新的工厂类来更改系统。如果您通过另一个交换工厂的表示,则可以在不接触大部分代码的情况下交换一整套产品。
生成器模式:GoF:“将复杂对象的构造与其表示分离,以便相同的构造过程可以创建不同的表示。”
这意味着什么:您将构建过程封装在另一个类中,称为director(GoF)。该导向器包含创建产品新实例的算法(例如,用其他部件组成一个复杂的产品)。为了创建整个产品的组成部分,导演使用了一个生成器。通过在director中交换生成器,您可以使用相同的算法来创建产品,但更改单个零件的表示(以及产品的表示)。要在产品的表示中扩展或修改系统,只需实现一个新的生成器类。
简而言之:抽象工厂模式的目的是交换一组共同使用的产品。生成器模式的目的是封装创建产品的抽象算法,以将其用于产品的不同表示。
在我看来,你不能说抽象工厂模式是生成器模式的大哥。是的,它们都是创造模式,但模式的主要意图完全不同。
生成器模式和工厂模式看起来都很像肉眼,因为它们都为您创建对象。
但你需要仔细观察
这个现实生活中的例子将使两者之间的区别更加明显。
假设你去了一家快餐店,点了食物。
1) 什么食物?
披萨
2) 什么浇头?
辣椒,番茄,烤鸡,无菠萝
因此,不同种类的食物是通过工厂模式制作的,但特定食物的不同变体(口味)是通过构建模式制作的。
不同种类的食物
披萨、汉堡、意大利面
披萨的变体
只有奶酪、奶酪+番茄+辣椒、奶酪+西红柿等。
代码示例
您可以在这里看到这两种模式的示例代码实现生成器模式工厂模式
IMHO
Builder是某种更复杂的工厂。
但在Builder中,您可以使用另一个工厂来实例化对象,这是构建最终有效对象所必需的。
所以,谈论“创造性模式”的复杂性演变,你可以这样想:
Dependency Injection Container -> Service Locator -> Builder -> Factory
Builder | Factory |
---|---|
Return only single instance to handle complex object construction | Return various instances on multiple constructors |
No interface required | Interface driven |
Inner classes is involved (to avoid telescopic constructors) | Subclasses are involved |
伸缩构造器模式
类比:
工厂:考虑一家餐馆。创建“今天的饭”是一种工厂模式,因为你告诉厨房“给我今天的饭吃”,厨房(工厂)根据隐藏的标准决定生成什么对象。生成器:如果您订购自定义披萨,则会显示生成器。在这种情况下,服务员告诉厨师(构建者)“我需要一个比萨饼;在其中添加奶酪、洋葱和培根!”因此,构建者公开了生成对象应该具有的属性,但隐藏了如何设置这些属性。
礼貌
复杂构造是指要构造的对象由抽象表示的不同的其他对象组成。
考虑麦当劳的菜单。菜单包括饮料、主菜和配菜。根据单个抽象的后代组合在一起,创建的菜单具有另一种表示。
例如:可乐、巨无霸、薯条示例:雪碧、掘金、卷曲薯条
在那里,我们得到了两个具有不同表示的菜单实例。施工过程保持不变。您可以创建一个菜单,其中包含饮料、主菜单和副菜单。
通过使用生成器模式,可以将创建复杂对象的算法与用于创建它的不同组件分开。
就构建器模式而言,算法封装在控制器中,而构建器用于创建完整的部分。在控制器的算法中改变使用的生成器会导致不同的表示,因为其他部分都是由菜单组成的。菜单的创建方式保持不变。
工厂:用于创建对象的实例,其中对象的依赖项完全由工厂保存。对于抽象工厂模式,通常有许多相同抽象工厂的具体实现。工厂的正确实现是通过依赖注入注入的。
生成器:用于构建不可变对象,当要实例化的对象的依赖项部分是预先知道的,部分是由生成器的客户端提供的。
它们之间的主要区别在于,生成器模式主要描述一步一步创建复杂对象。在抽象工厂模式中,重点是对象系列产品。生成器在最后一步返回产品。在抽象工厂模式中,产品立即可用。
例子:假设我们正在创建迷宫
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();
}
与工厂模式相比,构建器模式的主要优势在于,如果您希望创建一些具有大量可能自定义的标准对象,但最终通常只能自定义少数对象。
例如,如果你想写一个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专注于逐步构建复杂对象。
实施:
明确定义构建所有可用产品表示的通用构建步骤。否则,您将无法继续实现该模式。在基本生成器接口中声明这些步骤。为每个产品表示创建一个具体的生成器类,并实现它们的构造步骤。
抽象工厂专门创建相关对象的族。Abstract Factory会立即返回产品,而Builder允许您在获取产品之前运行一些额外的构建步骤。
您可以将抽象工厂与Bridge一起使用。当Bridge定义的某些抽象只能用于特定实现时,这种配对非常有用。在这种情况下,抽象工厂可以封装这些关系,并从客户端代码中隐藏复杂性。
深入设计模式