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

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

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示例吗?


当前回答

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

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

你有一个系统,它与另一个系统相连接。我将使用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。这是一个有点过时的文本,所以它可能很难理解上下文。我记得我读过这一章,从这个例子中除了有两个工厂基类的实现之外,我几乎没有理解什么,它们返回不同的对象族。

其他回答

以下图片来自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

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

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

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

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

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

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

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

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

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

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

你有一个系统,它与另一个系统相连接。我将使用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。这是一个有点过时的文本,所以它可能很难理解上下文。我记得我读过这一章,从这个例子中除了有两个工厂基类的实现之外,我几乎没有理解什么,它们返回不同的对象族。

没有人引用过原书《设计模式:可重用的面向对象软件的元素》,这本书在“创建模式的讨论”一节的前两段给出了答案:

There are two common ways to parameterize a system by the classes of objects it creates. One way is to subclass the class that creates the objects; this corresponds to using the Factory Method (107) pattern. The main drawback of this approach is that it can require a new subclass just to change the class of the product. Such changes can cascade. For example, when the product creator is itself created by a factory method, then you have to override its creator as well. The other way to parameterize a system relies more on object composition: Define an object that’s responsible for knowing the class of the product objects, and make it a parameter of the system. This is a key aspect of the Abstract Factory (87), Builder (97), and Prototype (117) patterns. All three involve creating a new “factory object” whose responsibility is to create product objects. Abstract Factory has the factory object producing objects of several classes. Builder has the factory object building a complex product incrementally using a correspondingly complex protocol. Prototype has the factory object building a product by copying a prototype object. In this case, the factory object and the prototype are the same object, because the prototype is responsible for returning the product.