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

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

Factory - Creates objects without exposing the instantiation logic to the client and Refers to the newly created object through a common interface. Is a simplified version of Factory Method Factory Method - Defines an interface for creating objects, but let subclasses to decide which class to instantiate and Refers to the newly created object through a common interface. Abstract Factory - Offers the interface for creating a family of related objects, without explicitly specifying their classes.

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

有人能告诉我吗

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


当前回答

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对象

其他回答

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

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

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

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

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

这就是抽象工厂模式:

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中。

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

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

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

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

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

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

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

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

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

Source of Confusion: Often, one can call a class that used in "Factory Method" pattern as "Factory". This class is abstract by definition. That's why it easy to call this class "Abstract Factory". But it's just the name of the class; you shouldn't confuse it with "Abstract Factory" pattern (class name != pattern name). The "Abstract Factory" pattern is different - it does not use an abstract class; it defines an interface (not necessarily a programming language interface) for creating parts of a bigger object or objects that are related to each other or must be created in a particular way.

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对象

以下图片来自Vaskaran Sarcar的《c#设计模式》第二版:

1. 简单工厂模式

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

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

2. 工厂方法模式

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

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

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

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

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

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

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

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

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

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

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