没有一个答案能真正很好地解释抽象工厂——可能是因为这个概念是抽象的,而且在实践中使用较少。
考虑以下情况,可以举一个容易理解的例子。
你有一个系统,它与另一个系统相连接。我将使用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。这是一个有点过时的文本,所以它可能很难理解上下文。我记得我读过这一章,从这个例子中除了有两个工厂基类的实现之外,我几乎没有理解什么,它们返回不同的对象族。