Abstract Factory 抽象工厂
除夕前一天还在写学习笔记,我都佩服我自己了O(∩_∩)O
意图
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
抽象工厂将具体的类分离,因为一个工厂封装了创建产品对象的责任和过程,它将客户和类的实现分离。
一个具体工厂类只在应用初始化时出现一次,这使得换用另一个风格的工厂更加容易。由于一个抽象工厂创建了一个对象系列,因此更换工厂之后,工厂产品会立即发生改变。
同时,抽象工厂也可以解决产品匹配的问题。因为使用同一个工厂制造出来的产品,其风格一定是相同的,也就一定是互相兼容的。
从稳定-变化的角度来看,抽象工厂在产品种类稳定的情况下,支持不同风格同种产品间的方便替换。缺点是,抽象工厂并不支持简单的添加产品种类。
代码案例
现在有一个用户界面工具包,它支持多种视感,我们以Motif和Presentation Manager为例。在每种视感下,都有几种功能相同的控件,这里以窗口(Window)和菜单(Menu)为例。
下面是一种解决方案:
我们用如下方法去初始化:
class APP
{
void intiWindow()
{
...
Window *w;
w = new MotifWindow();
...
}
void intiMenu()
{
...
Menu *m
m = new MotifMenu();
...
}
}
显然,看上去,这样编写也没有什么问题。但是,首先,这使得用户(class APP编写者)需要显式地调用子类的构造函数,这可能违背了接口隔离原则,增加了程序的耦合度。再者,一旦我们需要变更产品风格,比如现在将控件风格变为PM,那么可能有很多的函数会被更改。甚至,如果我们忘记更改某一处产品创建的风格,而两种风格又是不兼容的,还很有可能导致非常难以察觉的Bug。
现在,我们意识到,如果我们选择了Motif风格,那么不管是Window, Menu,还是可能存在的其他产品,都一定是Motif风格的。既然如此,何不将所有产品的创建封装到一个类中呢?
下面是抽象工厂模式给出的解决方案。
解决方案:
class AbstractFactory{
virtual Window* createWindow() = 0;
virtual Menu* createMenu() = 0;
}
class MotifFactory
{
Window* createWindow(){
return new MotifWindow();
}
Menu* createMenu(){
return new MotifMenu;
}
}
class PMFactory
{
Window* createWindow(){
return new PMWindow();
}
Menu* createMenu(){
return new PMMenu;
}
}
那么,APP类只需要这样编写:
class APP
{
AbstractFactory* factory;
APP(AbstractFactoy af):factory(af){
}
void intiWindow()
{
...
Window *w;
w = factory->createWindow();
...
}
void intiMenu()
{
...
Menu *m
m = factory->createMenu();
...
}
}
只需要在初始化时传入一个工厂参数即可。
解释
你或许可以从下面的类图中更好地理解Abstract Factory。
抽象工厂模式和工厂方法模式异曲同工,它们的主要区别在于:工厂方法针对单一产品设计工厂,而抽象工厂模式则针对一种风格,也就是说,通常,工厂方法的类中只产出一种产品,而抽象工厂类产出同一风格的多种产品。抽象工厂的针对性更强。
本文中大量使用产品这个词,简单来讲,这个词指的就是对象。具有平行功能的对象,比如MotifWindow和PMWindow,就被分为不同的风格。
当然,也不得不提到这一模式的弊端:难以支持新种类的产品。比如,突然要给工具包中增加一个ScrollBar控件,那么AbstractFactory基类中就必须添加相应的函数,导致所有的子类一并需要更改。那么,当产品的变更经常发生时,Abstract Factory就不再适用了。
总结
设计模式 | Abstract Factory(抽象工厂) |
---|---|
稳定点: | 生产出的产品种类。 |
变化点: | 产品的风格。 |
效果: | 将同一风格的对象创建封装到一个类中。 |
特点: | 给定一个工厂,就可以调用它的方法便捷地从生产出同一风格的各种产品。 |
类图:
2021.2.10 转载请标明出处