1. 简介
抽象工厂模式是所有形态的工厂模式中最为抽象和最具一般性的一种形态。抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。
抽象工厂模式(Abstract Factory),提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- AbstractFactory(抽象工厂):声明一个创建抽象产品对象的操作接口。
- ConcreateFactory(具体工厂):实现创建具体产品对象的操作。
- AbstractProduct(抽象产品):为一类产品对象声明一个接口。
- ConcreateProduct(具体产品):定义一个将被相应的具体工厂创建的产品对象,实现AbstractProduct接口。
其中客户端仅使用AbstractFactory和AbstractProduct类声明的接口。
2. 结构图
此模式的结构如下图所示:
- AbstractFactory(抽象工厂):声明一个创建抽象产品对象的操作接口。
- ConcreateFactory(具体工厂):实现创建具体产品对象的操作。
- AbstractProduct(抽象产品):为一类产品对象声明一个接口。
- ConcreateProduct(具体产品):定义一个将被相应的具体工厂创建的产品对象,实现AbstractProduct接口。
其中客户端仅使用AbstractFactory和AbstractProduct类声明的接口。
3. 案例分析
考虑一个支持多种视感标准的用户界面工具包,例如 Motif 和 Presentation Manager 。不同的视感风格为诸如滚动条、窗口和按钮等用户界面“窗口组件”定义不同的外观和行为。为保证视感风格标准间的可移植性,一个应用不应该为一个特定的视感外观硬编码它的窗口组件。在整个应用中实例化特定视感风格的窗口组件类将使得以后很难改变视感风格。
为解决这一个问题我们可以定义一个抽象的WidgetFactory类,这个类声明了一个用来创建每一类基本窗口组件的接口。每一类窗口组件都有一个抽象类,而具体子类则实现了窗口组件的特定视感风格。对于每一个抽象窗口组件类,WidgetFactory接口都有一个返回新窗口组件对象的操作。客户调用这些操作以获取窗口组件实例,但客户并不知道他们正在使用的是哪些具体类。这样客户就不依赖于一般的视感风格。
每一种视感标志都对应于一个具体的WidegetFactory子类。每一子类实现那些用于创建合适视感风格的窗口组件的操作。例如,MotifWidgetFactory的CreateScrollBar操作实例化并返回一个Motif滚动条,而相应的PMWidgetFactory操作返回一个Presentation Manager的滚动条。客户仅通过WidgetFactory接口创建窗口组件,他们并不知道哪些类实现了特定视感风格的窗口组件。换言之,客户仅与抽象类定义的接口交互,而不使用特定的具体类的接口。
/*********************************************************************************
*Copyright(C),Your Company
*FileName: WidgetFactory.h
*Author: Huangjh
*Version:
*Date: 2017-10-28
*Description: 声明一个创建抽象产品对象的接口——抽象工厂
*Others:
**********************************************************************************/
#ifndef _WIDGET_FACTORY_H
#define _WIDGET_FACTORY_H
class CScrollBar;
class CButton;
class CWidgetFactory
{
public:
CWidgetFactory() { }
virtual ~CWidgetFactory() { }
virtual CScrollBar *CreateScrollBar() = 0;
virtual CButton *CreateButtdon() = 0;
};
#endif //#ifndef _WIDGET_FACTORY_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: MotifyWidgetFactory.h
*Author: Huangjh
*Version:
*Date: 2017-10-28
*Description: 实现创建具体产品对象的的操作(Motify风格)——具体工厂
*Others:
**********************************************************************************/
#ifndef _MOTIFY_WIDGET_FACTORY_H
#define _MOTIFY_WIDGET_FACTORY_H
#include "WidgetFactory.h"
#include "MotifyButton.h"
#include "MotifyScrollBar.h"
class CMotifyWidgetFactory : public CWidgetFactory
{
public:
CMotifyWidgetFactory() { }
~CMotifyWidgetFactory() { }
CScrollBar *CreateScrollBar(void)
{
return new CMotifyScrollBar();
}
CButton *CreateButtdon(void)
{
return new CMotifyButton();
}
};
#endif //#ifndef _MOTIFY_WIDGET_FACTORY_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: PMWidgetFactory.h
*Author: Huangjh
*Version:
*Date: 2017-10-28
*Description: 实现创建具体产品对象的的操作(Presentation Manage风格)——具体工厂
*Others:
**********************************************************************************/
#ifndef _PM_WIDGET_FACTORY_H
#define _PM_WIDGET_FACTORY_H
#include "WidgetFactory.h"
#include "PMButton.h"
#include "PMScrollBar.h"
class CPMWidgetFactory : public CWidgetFactory
{
public:
CPMWidgetFactory() { }
~CPMWidgetFactory() { }
CScrollBar *CreateScrollBar(void)
{
return new CPMScrollBar();
}
CButton *CreateButtdon(void)
{
return new CPMButton();
}
};
#endif //#ifndef _PM_WIDGET_FACTORY_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: ScrollBar.h
*Author: Huangjh
*Version:
*Date: 2017-10-28
*Description: 为滚动条窗口这类对象声明接口——抽象产品
*Others:
**********************************************************************************/
#ifndef _SCROLL_BAR_H
#define _SCROLL_BAR_H
class CScrollBar
{
public:
CScrollBar() { }
virtual ~CScrollBar() { }
virtual void DisplayScrollBar(void) = 0;
};
#endif //#ifndef _SCROLL_BAR_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: Button.h
*Author: Huangjh
*Version:
*Date: 2017-10-28
*Description: 为按钮这类对象声明接口——抽象产品
*Others:
**********************************************************************************/
#ifndef _BUTTON_H
#define _BUTTON_H
class CButton
{
public:
CButton() { }
virtual ~CButton() { }
virtual void DisplayButton(void) = 0;
};
#endif //#ifndef _BUTTON_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: MotifyScrollBar.h
*Author: Huangjh
*Version:
*Date: 2017-10-28
*Description: 定义一个将被MotifyWidgetFactory工厂创建的Motify风格滚动条——具体产品
*Others:
**********************************************************************************/
#ifndef _MOTIFY_SCROLL_BAR_H
#define _MOTIFY_SCROLL_BAR_H
#include <iostream>
#include "ScrollBar.h"
class CMotifyScrollBar : public CScrollBar
{
public:
CMotifyScrollBar() { }
~CMotifyScrollBar() { }
void DisplayScrollBar(void)
{
std::cout << "显示一个Motify风格的滚动条" << std::endl;
}
};
#endif //#ifndef _MOTIFY_SCROLL_BAR_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: MotifyButton.h
*Author: Huangjh
*Version:
*Date: 2017-10-28
*Description: 定义一个将被MotifyWidgetFactory工厂创建的Motify风格按钮——具体产品
*Others:
**********************************************************************************/
#ifndef _MOTIFY_BUTTON_H
#define _MOTIFY_BUTTON_H
#include <iostream>
#include "Button.h"
class CMotifyButton : public CButton
{
public:
CMotifyButton() { }
~CMotifyButton() { }
void DisplayButton(void)
{
std::cout << "显示一个Motify风格的按钮" << std::endl;
}
};
#endif //#ifndef _MOTIFY_BUTTON_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: PMScrollBar.h
*Author: Huangjh
*Version:
*Date: 2017-10-28
*Description: 定义一个将被PMWidgetFactory工厂创建的风格滚动条——具体产品
*Others:
**********************************************************************************/
#ifndef _PM_SCROLL_BAR_H
#define _PM_SCROLL_BAR_H
#include <iostream>
#include "ScrollBar.h"
class CPMScrollBar : public CScrollBar
{
public:
CPMScrollBar() { }
~CPMScrollBar() { }
void DisplayScrollBar(void)
{
std::cout << "显示一个Presentation Manage风格的滚动条" << std::endl;
}
};
#endif //#ifndef _PM_SCROLL_BAR_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: PMButton.h
*Author: Huangjh
*Version:
*Date: 2017-10-28
*Description: 定义一个将被PMWidgetFactory工厂创建的风格按钮——具体产品
*Others:
**********************************************************************************/
#ifndef _PM_BUTTON_H
#define _PM_BUTTON_H
#include <iostream>
#include "Button.h"
class CPMButton : public CButton
{
public:
CPMButton() { }
~CPMButton() { }
void DisplayButton(void)
{
std::cout << "显示一个Presentation Manage风格的按钮" << std::endl;
}
};
#endif //#ifndef _PM_BUTTON_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName: main.cpp
*Author: Huangjh
*Version:
*Date: 2017-10-28
*Description: 抽象工厂模式的测试用例
*Others:
**********************************************************************************/
#include "PMWidgetFactory.h"
#include "MotifyWidgetFactory.h"
int main(void)
{
CWidgetFactory *pWidgetFactory = new CMotifyWidgetFactory();
//CWidgetFactory *pPMFactory = new CPMWidgetFactory();
CScrollBar *pScrollBar = pWidgetFactory->CreateScrollBar();
pScrollBar->DisplayScrollBar();
CButton *pButton = pWidgetFactory->CreateButtdon();
pButton->DisplayButton();
delete pScrollBar;
delete pButton;
delete pWidgetFactory;
return 0;
}
运行结果如下所示:
显示一个Motify风格的滚动条
显示一个Motify风格的按钮
下面是类视图:
4. 优缺点
4.1 优点
- 它分离了具体的类。Abstract Factory 模式帮助你控制一个应用创建的对象的类。因为一个工厂封装创建产品对象的责任和过程,它将客户与类的实现分离。客户通过它们的抽象接口操纵实例。产品的类名也在具体的工厂的实现中被分离;它们不出现在客户代码中。
- 它使得易于交换产品系列。一个具体工厂类在一个应用中仅出现一次——即在它初始化的时候。这使得改变一个应用的具体工厂变得很容易。它只需改变具体的工厂即可使用不同的产品配置,这是因为 一个抽象工厂创建了一个完整的产品系列,所以整个产品系列会立刻改变。
- 它有利于产品的一致性。当一个系列中的产品对象被设计成一起工作时,一个应用一次只能使用同一系列中的对象,这一点很重要。
4.2 缺点
- 难以支持新种类的产品。难以扩展抽象工厂以生产新种类的产品。这是因为Abstract Factory接口确定了可以被创建的产品集合。支持新种类产品就需要扩展该工厂接口,这将涉及Abstract Factory类及其所有子类的改变。
5. 适用性
在以下情况下可以使用Abstract Factory模式
- 一个系统要独立于它的产品的创建、组合和表示时。
- 一个系统要由多个产品体系中的一个来配置时。
- 当你要强调一系列相关的产品对象的设计以便进行联合使用时。
- 当你提供一个产品类库,而只想显示它们的接口而不是实现时。
6. 参考数据
- 《大话设计模式》
- 《设计模式——可复用的面向对象的软件基础》