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

我从一个网站上阅读设计模式

在那里我读到工厂,工厂方法和抽象工厂,但他们是如此混乱,我不清楚的定义。根据定义

Factory—创建对象而不向客户端公开实例化逻辑,并通过公共接口引用新创建的对象。是工厂方法的简化版吗

Factory方法——定义一个用于创建对象的接口,但是让子类来决定实例化哪个类,并通过公共接口引用新创建的对象。

抽象工厂——提供了创建一系列相关对象的接口,而无需显式地指定它们的类。

我还看了其他关于抽象工厂和工厂方法的stackoverflow线程,但那里绘制的UML图使我的理解更加糟糕。

有人能告诉我吗

  1. 这三种模式之间有什么不同?
  2. 什么时候用哪个?
  3. 如果可能的话,还有与这些模式相关的java示例吗?
117967 次浏览

工厂 -分离工厂类来创建复杂的对象。

例如:FruitFactory类来创建Fruit对象

class FruitFactory{


public static Fruit getFruit(){...}


}

工厂方法 -不用为工厂添加一个单独的类,只需在类中添加一个方法作为工厂。

例:

Calendar.getInstance() (Java's Calendar)

抽象工厂 -工厂的工厂

比方说,我们想建一家生产电脑零件的工厂。有几种类型的电脑,如笔记本电脑,台式电脑,服务器。

所以对于每一种计算机类型,我们都需要工厂。所以我们创建了一个高级的工厂中的工厂,如下所示

ComputerTypeAbstractFactory.getComputerPartFactory(String computerType) ---> This will return PartFactory which can be one of these ServerPartFactory, LaptopPartFactory, DesktopPartFactory.

现在这3家公司本身也是工厂。(您将处理PartFactory本身,但在底层,将有基于您在抽象工厂中提供的内容的单独实现)

  Interface-> PartFactory. getComputerPart(String s),
Implementations -> ServerPartFactory, LaptopPartFactory, DesktopPartFactory.


Usage:
new ComputerTypeAbstractFactory().getFactory(“Laptop”).getComputerPart(“RAM”)

EDIT:根据注释中的异议为抽象工厂提供精确的接口。

每个设计模式都努力确保编写的工作代码不受影响。我们都知道,一旦我们接触到工作代码,现有的工作流程中就会有缺陷,并且需要进行更多的测试以确保我们没有破坏任何东西。

工厂模式根据输入条件创建对象,从而确保你不需要编写如下代码:

 if (this) {
create this kind of object
} else {
that kind of object
}

一个很好的例子就是旅游网站。旅游网站只能提供旅行(航班、火车、巴士)或/和提供酒店或/和提供旅游景点套餐。现在,当用户选择下一步时,网站需要决定需要创建什么对象。它是否也只创建travel或hotel对象?

现在,如果你设想在你的投资组合中添加另一个网站,并且你相信同样的核心可以被使用,例如,一个拼车网站,现在可以搜索出租车并在线支付,你可以在你的核心中使用一个抽象工厂。这样一来,你就可以再加入一个出租车和拼车工厂。

这两个工厂之间没有任何关系,所以把它们放在不同的工厂是一个很好的设计。

希望大家都明白了。再次研究这个网站,记住这个例子,希望它会有帮助。我真的希望我已经正确地表示了模式:)。

所有三种工厂类型都做同样的事情:它们是“聪明的构造器”。

假设您希望能够创建两种水果:Apple和Orange。

工厂

Factory是“固定的”,因为你只有一个没有子类化的实现。在这种情况下,你会有一个这样的类:
class FruitFactory {


public Apple makeApple() {
// Code for creating an Apple here.
}


public Orange makeOrange() {
// Code for creating an orange here.
}


}

用例:在构造函数中构造Apple或Orange都太复杂了。

工厂方法

当你在一个类中有一些泛型处理,但想要改变你实际使用的水果类型时,通常使用Factory方法。所以:
abstract class FruitPicker {


protected abstract Fruit makeFruit();


public void pickFruit() {
private final Fruit f = makeFruit(); // The fruit we will work on..
<bla bla bla>
}
}

...那么你可以通过在子类中实现一个工厂方法来重用FruitPicker.pickFruit()中的公共功能:

class OrangePicker extends FruitPicker {


@Override
protected Fruit makeFruit() {
return new Orange();
}
}

抽象工厂

抽象工厂通常用于依赖注入/策略之类的事情,当你想要能够创建一个“同类”的对象家族,并且有一些公共的基类时。这里有一个与水果有关的例子。这里的用例是我们想要确保我们不会意外地在Apple上使用OrangePicker。只要我们的Fruit和Picker来自同一家工厂,它们就会匹配。
interface PlantFactory {
  

Plant makePlant();


Picker makePicker();


}


public class AppleFactory implements PlantFactory {
Plant makePlant() {
return new Apple();
}


Picker makePicker() {
return new ApplePicker();
}
}


public class OrangeFactory implements PlantFactory {
Plant makePlant() {
return new Orange();
}


Picker makePicker() {
return new OrangePicker();
}
}
AbstractProductA, A1 and A2 both implementing the AbstractProductA
AbstractProductB, B1 and B2 both implementing the AbstractProductB


interface Factory {
AbstractProductA getProductA(); //Factory Method - generate A1/A2
}

使用工厂方法,用户可以创建AbstractProductA的A1或A2。

interface AbstractFactory {
AbstractProductA getProductA(); //Factory Method
AbstractProductB getProductB(); //Factory Method
}
但是抽象工厂有多个工厂方法(例如:2个工厂方法),使用这些工厂方法它将创建对象/相关对象的集合。 使用抽象工厂,用户可以创建AbstractProductA, AbstractProductB

的A1, B1对象
  1. 这三种模式之间有什么不同?

工厂:创建对象而不向客户端公开实例化逻辑。

定义一个创建对象的接口,但是让子类来决定实例化哪个类。Factory方法允许类延迟实例化到子类

抽象工厂:提供了一个接口,用于创建相关或依赖的对象族,而不指定它们的具体类。

AbstractFactory模式使用组合将创建对象的责任委托给另一个类,而工厂方法设计模式使用继承并依赖于派生类或子类来创建对象

  1. 什么时候用哪个?

工厂:客户端只需要一个类,并不关心它得到的具体实现。

工厂方法:客户端不知道在运行时需要创建什么具体的类,但只是想获得一个将完成这项工作的类。

AbstactFactory:当你的系统必须创建多个产品家族,或者你想提供一个产品库而不暴露实现细节时。

抽象工厂类通常用工厂方法实现。工厂方法通常在模板方法中调用。

  1. 如果可能的话,还有与这些模式相关的java示例吗?

Factory和FactoryMethod

< em >目的:< / em >

定义一个用于创建对象的接口,但是让子类来决定实例化哪个类。工厂方法允许类延迟实例化到子类。

UML图:

enter image description here

产品:它定义Factory方法创建的对象的接口。

实现Product接口

创造者:声明Factory方法

ConcreateCreator:实现Factory方法以返回ConcreteProduct的实例

问题陈述:使用定义游戏界面的工厂方法创建游戏工厂。

代码片段:

工厂模式。什么时候使用工厂方法?< / >

与其他创造模式的比较:

  1. 设计开始时使用<强>工厂方法(不太复杂,更可定制,子类激增),然后随着设计师发现需要更多灵活性的地方,逐步向<强>抽象工厂、原型或构建器(更灵活,更复杂)发展

  2. 抽象工厂类通常使用工厂方法实现,但它们也可以使用原型实现

进一步阅读的参考:Sourcemaking设计模式

要回答这个问题,我可以参考《四人帮》一书。

书中有没有“工厂”或“简单工厂”或“虚拟工厂”的定义。通常当人们谈论“工厂”模式时,他们可能在谈论创建类的特定对象的东西(而不是“建造者”模式);他们可能,也可能不指的是“工厂方法”或“抽象工厂”模式。任何人都可以实现“工厂”,但他不会,因为它不是一个正式的术语(请记住,一些人、公司、社区可以有自己的词汇表)。

本书只有包含了“抽象工厂”和“工厂方法”的定义。

以下是书中的定义,并简要解释了为什么两者都如此令人困惑。我省略了代码示例,因为你可以在其他答案中找到它们:

工厂法(GOF):定义一个创建对象的接口,但是让子类来决定实例化哪个类。工厂方法允许类延迟实例化到子类。

抽象工厂(GOF):提供一个接口来创建相关或依赖的对象族,而不指定它们的具体类。

困惑的根源:通常,可以将“Factory Method”模式中使用的类称为“Factory”。这个类根据定义是抽象的。这就是为什么很容易称这个类为“抽象工厂”。但这只是类的名字;你不应该将它与“抽象工厂”模式(类名!=模式名)混淆。“抽象工厂”模式是不同的-它使用一个抽象类;它定义了一个接口(不一定是编程语言接口),用于创建较大对象的各个部分,或者创建相互关联或必须以特定方式创建的对象。

没有人引用原著设计模式:可重用面向对象软件的元素,它在“讨论创造模式”部分的前两段给出了答案(强调我的):

有两种常用方法通过系统创建的对象的类来参数化系统。一种方法是子类创建对象的类;这对应于使用工厂方法(107)模式。这种方法的主要缺点是,它可能需要一个新的子类来更改产品的类。这样的变化可以级联。例如,当产品创建者本身是由工厂方法创建时,那么您也必须重写它的创建者。

另一种参数化系统的方法更多地依赖于对象作文:定义一个对象,它负责知道产品对象的类,并使其成为系统的参数。这是抽象工厂(87)、构建器(97)和原型(117)模式的一个关键方面。这三个都涉及到创建一个新的“工厂对象”,其职责是创建产品对象。抽象工厂的工厂对象可以产生多个类的对象。Builder让工厂对象使用相应的复杂协议增量地构建复杂产品。Prototype让工厂对象通过复制原型对象来构建产品。在这种情况下,工厂对象和原型是相同的对象,因为原型负责返回产品。

基于设计模式在c#,第二版由Vaskaran Sarcar书中的图像:

1. 简单工厂模式

创建对象而不向客户端公开实例化逻辑。

SimpleFactory simpleFactory = new SimpleFactory();
IAnimal dog = simpleFactory.CreateDog(); // Create dog
IAnimal tiger = simpleFactory.CreateTiger(); // Create tiger

enter image description here

2. 工厂方法模式

定义一个用于创建对象的接口,但是让子类来决定实例化哪个类。

AnimalFactory dogFactory = new DogFactory();
IAnimal dog = dogFactory.CreateAnimal(); // Create dog


AnimalFactory tigerFactory = new TigerFactory();
IAnimal tiger = tigerFactory.CreateAnimal(); // Create tiger

enter image description here

3.抽象工厂模式(工厂的工厂)

抽象工厂提供了创建一系列相关对象的接口,而无需显式地指定它们的类

IAnimalFactory petAnimalFactory = FactoryProvider.GetAnimalFactory("pet");
IDog dog = petAnimalFactory.GetDog(); // Create pet dog
ITiger tiger = petAnimalFactory.GetTiger();  // Create pet tiger


IAnimalFactory wildAnimalFactory = FactoryProvider.GetAnimalFactory("wild");
IDog dog = wildAnimalFactory .GetDog(); // Create wild dog
ITiger tiger = wildAnimalFactory .GetTiger();  // Create wild tiger

enter image description here

没有一个答案能真正很好地解释抽象工厂——可能是因为这个概念是抽象的,而且在实践中使用较少。

考虑以下情况,可以举一个容易理解的例子。

你有一个系统,它与另一个系统相连接。我将使用Shalloway和Trott在《设计模式解释》(P194)中给出的例子,因为这种情况非常罕见,我想不出更好的例子了。在他们的书中,他们给出了本地硬件资源的不同组合的例子。他们举例说:

  • 系统具有高分辨率显示和打印驱动程序
  • 系统具有低分辨率显示和打印驱动程序

一个变量有两个选项(打印驱动程序、显示驱动程序),另一个变量有两个选项(高分辨率、低分辨率)。我们希望以这样的方式将它们耦合在一起,即我们有HighResolutionFactoryLowResolutionFactory,它们为我们生成正确类型的打印驱动程序和显示驱动程序。

这就是抽象工厂模式:

class ResourceFactory
{
virtual AbstractPrintDriver getPrintDriver() = 0;


virtual AbstractDisplayDriver getDisplayDriver() = 0;
};


class LowResFactory : public ResourceFactory
{
AbstractPrintDriver getPrintDriver() override
{
return LowResPrintDriver;
}


AbstractDisplayDriver getDisplayDriver() override
{
return LowResDisplayDriver;
}
};


class HighResFactory : public ResourceFactory
{
AbstractPrintDriver getPrintDriver() override
{
return HighResPrintDriver;
}


AbstractDisplayDriver getDisplayDriver() override
{
return HighResDisplayDriver;
}
};


我不会详细说明打印驱动程序和显示驱动程序的层次结构,只需要一个就足够演示了。

class AbstractDisplayDriver
{
virtual void draw() = 0;
};


class HighResDisplayDriver : public AbstractDisplayDriver
{
void draw() override
{
// do hardware accelerated high res drawing
}
};


class LowResDisplayDriver : public AbstractDisplayDriver
{
void draw() override
{
// do software drawing, low resolution
}
};

为什么有效:

我们本可以用一堆if语句来解决这个问题:

const resource_type = LOW_RESOLUTION;


if(resource_type == LOW_RESOLUTION)
{
drawLowResolution();
printLowResolution();
}
else if(resource_type == HIGH_RESOLUTION)
{
drawHighResolution();
printHighResolution();
}

现在我们可以这样做:

    auto factory = HighResFactory;
auto printDriver = factory.getPrintDriver();
printDriver.print();
auto displayDriver = factory.getDisplayDriver();
displayDriver.draw();

本质上——我们已经将运行时逻辑抽象到类的v-table中。

我对这种模式的看法是,它实际上并不是很有用。它把一些不需要结合的东西结合在一起。设计模式的重点通常是减少耦合,而不是增加耦合,因此在某些方面,此模式实际上是反模式,但在某些上下文中可能有用。

如果您到了认真考虑实现此功能的阶段,您可能会考虑一些替代设计。也许你可以编写一个返回工厂的工厂,工厂本身返回你最终想要的对象。我认为这将更加灵活,并且不会有相同的耦合问题。

附录:“四人帮”的例子也有类似的耦合。它们有MotifFactoryPMFactory。然后它们分别产生PMWindowPMScrollBarMotifWindowMotifScrollBar。这是一个有点过时的文本,所以它可能很难理解上下文。我记得我读过这一章,从这个例子中除了有两个工厂基类的实现之外,我几乎没有理解什么,它们返回不同的对象族。