1、工厂方法模式概述
在简单工厂模式中只提供一个工厂类,该工厂类处于对产品类进行实例化的中心位置,它需要知道每一个产品对象的创建细节,并决定何时实例化哪一个产品类。简单工厂模式最大的缺点是当有新产品要加入到系统中时,必须修改工厂类,需要在其中加入必要的业务逻辑,这违背了“开闭原则”。此外,在简单工厂模式中,所有的产品都由同一个工厂创建,工厂类职责较重,业务逻辑较为复杂,具体产品与工厂类之间的耦合度高,严重影响了系统的灵活性和扩展性,而工厂方法模式则可以很好地解决这一问题。
在工厂方法模式中,我们不再提供一个统一的工厂类来创建所有的产品对象,而是针对不同的产品提供不同的工厂,系统提供一个与产品等级结构对应的工厂等级结构。工厂方法模式定义如下:
工厂方法模式(Factory Method Pattern):定义一个用于创建对象的接口,让子类决定将哪一个类实例化。工厂方法模式让一个类的实例化延迟到其子类。工厂方法模式又简称为工厂模式(Factory Pattern),是一种类创建型模式。 |
工厂方法模式提供一个抽象工厂接口来声明抽象工厂方法,而由其子类来具体实现工厂方法,创建具体的产品对象。工厂方法模式结构如图所示:
工厂方法模式结构图
在工厂方法模式结构图中包含如下几个角色:
Product(抽象产品):它是定义产品的接口,是工厂方法模式所创建对象的超类型,也就是产品对象的公共父类。
ConcreteProduct(具体产品):它实现了抽象产品接口,某种类型的具体产品由专门的具体工厂创建,具体工厂和具体产品之间一一对应。
Factory(抽象工厂):在抽象工厂类中,声明了工厂方法(Factory Method),用于返回一个产品。抽象工厂是工厂方法模式的核心,所有创建对象的工厂类都必须实现该接口。
ConcreteFactory(具体工厂):它是抽象工厂类的子类,实现了抽象工厂中定义的工厂方法,并可由客户端调用,返回一个具体产品类的实例。
2、QQ空间背景风格的设计与实现
项目需求:QQ空间提供了多种背景风格供用户选择,用户可以根据需要选择不同风格类型的背景装扮空间。现模仿QQ空间背景风格的实现,设计一套能够让用户自由选择的背景风格。要求系统具有弹性,能够拓展任意类型的背景风格。
2.1背景风格类
背景风格类实现代码如下:
#ifndef _BACKGROUND_STYLE_H_
#define _BACKGROUND_STYLE_H_
#include <iostream>
#include <string>
using namespace std;
//背景风格抽象类
class BackgroundStyle
{
public:
//虚方法,显示背景风格
virtual void DisplayStyle() = 0;
};
//古典风格背景类
class ClassicalStyle : public BackgroundStyle
{
public:
void DisplayStyle()
{
cout << "古典风格背景" << endl;
}
};
//潮流风格背景类
class FashionStyle : public BackgroundStyle
{
public:
void DisplayStyle()
{
cout << "潮流风格背景" << endl;
}
};
//艺术风格背景类
class ArtStyle : public BackgroundStyle
{
public:
void DisplayStyle()
{
cout << "艺术风格背景" << endl;
}
};
#endif
BackgroundStyle背景风格抽象类是抽象的产品类,提供了一个DisplayStyle方法,显示具体的QQ背景风格。ClassicalStyle古典风格背景类、FashionStyle潮流风格背景类、ArtStyle艺术风格背景类是具体的产品类。
2.2 背景风格工厂
背景风格工厂类实现代码如下:
#ifndef _STYLE_FACTORY_H_
#define _STYLE_FACTORY_H_
#include "backgroundStyle.h"
//背景风格抽象工厂
class StyleFactory
{
public:
//工厂方法,具体背景风格由子类完成创建操作
virtual BackgroundStyle * CreateBackGroundStyle() = 0;
};
//古典风格工厂
class ClassicalStyleFactory : public StyleFactory
{
public:
BackgroundStyle * CreateBackGroundStyle()
{
BackgroundStyle * pClassicalStyle = new ClassicalStyle();
return pClassicalStyle;
}
};
//潮流风格工厂
class FashionStyleFactory : public StyleFactory
{
public:
BackgroundStyle * CreateBackGroundStyle()
{
BackgroundStyle * pFashionStyle = new FashionStyle();
return pFashionStyle;
}
};
//艺术风格工厂
class ArtStyleFactory : public StyleFactory
{
public:
BackgroundStyle * CreateBackGroundStyle()
{
BackgroundStyle * pArtStyle = new ArtStyle();
return pArtStyle;
}
};
#endif
StyleFactory背景风格抽象工厂,该抽象类中提供一个工厂方法CreateBackGroundStyle,用于创建具体的QQ背景样式。具体创建QQ背景样式的过程由子类工厂去实现。具体的工厂如:ClassicalStyleFactory古典风格工厂,创建古典风格的背景样式;FashionStyleFactory潮流风格工厂,创建潮流风格背景样式;ArtStyleFactory艺术风格工厂,创建艺术风格的背景样式。
#include <iostream>
#include "backgroundStyle.h"
#include "StyleFactory.h"
using namespace std;
int main()
{
BackgroundStyle * pBackgroundStyle = NULL;
/**********************创建古典风格背景***************************/
StyleFactory * pClassicalStyleFactory = new ClassicalStyleFactory();
pBackgroundStyle = pClassicalStyleFactory->CreateBackGroundStyle();
pBackgroundStyle->DisplayStyle();
delete pClassicalStyleFactory;
pClassicalStyleFactory = NULL;
delete pBackgroundStyle;
pBackgroundStyle = NULL;
/**********************创建潮流风格工厂***************************/
StyleFactory * pFashionStyleFactory = new FashionStyleFactory();
pBackgroundStyle = pFashionStyleFactory->CreateBackGroundStyle();
pBackgroundStyle->DisplayStyle();
delete pFashionStyleFactory;
pFashionStyleFactory = NULL;
delete pBackgroundStyle;
pBackgroundStyle = NULL;
/**********************创建艺术风格工厂***************************/
StyleFactory * pArtStyleFactory = new ArtStyleFactory();
pBackgroundStyle = pArtStyleFactory->CreateBackGroundStyle();
pBackgroundStyle->DisplayStyle();
delete pArtStyleFactory;
pArtStyleFactory = NULL;
delete pBackgroundStyle;
pBackgroundStyle = NULL;
return 0;
}
编译并执行,结果如下:
2.3 后期扩展与总结
如果需要添加其它风格的QQ背景,只需要定义一个其它风格的背景类,继承于抽象背景风格类;定义一个对应的工厂类,继承于抽象背景风格工厂类就可以了,不需要修改之前的代码,符合"开放封闭原则"。对于每一个背景风格类,都有一个创建该背景风格的工厂类与之相对应,符合"单一原则"。客户端只需要认识抽象的背景风格类就可以了,不需要认识具体背景风格类,降低了客户端和具体背景风格类的耦合度。也就是说客户端针对接口进行编程,符合"针对接口进行编程而不是针对具体进行编程原则"。3、工厂方法的隐藏
有时候,为了进一步简化客户端的使用,还可以对客户端隐藏工厂方法,此时,在工厂类中将直接调用产品类的业务方法,客户端无须调用工厂方法创建产品,直接通过工厂即可使用所创建的对象中的业务方法。
3.1 背景风格类
背景风格类并没有变化,实现代码如下:
#ifndef _BACKGROUND_STYLE_H_
#define _BACKGROUND_STYLE_H_
#include <iostream>
#include <string>
using namespace std;
//背景风格抽象类
class BackgroundStyle
{
public:
//虚方法,显示背景风格
virtual void DisplayStyle() = 0;
};
//古典风格背景类
class ClassicalStyle : public BackgroundStyle
{
public:
void DisplayStyle()
{
cout << "古典风格背景" << endl;
}
};
//潮流风格背景类
class FashionStyle : public BackgroundStyle
{
public:
void DisplayStyle()
{
cout << "潮流风格背景" << endl;
}
};
//艺术风格背景类
class ArtStyle : public BackgroundStyle
{
public:
void DisplayStyle()
{
cout << "艺术风格背景" << endl;
}
};
#endif
3.2 背景风格工厂类
背景风格工厂类实现代码如下:
#ifndef _STYLE_FACTORY_H_
#define _STYLE_FACTORY_H_
#include "backgroundStyle.h"
//背景风格抽象工厂
class StyleFactory
{
private:
//保存创建的背景风格
BackgroundStyle * pBackgroundStyle;
public:
StyleFactory()
{
pBackgroundStyle = NULL;
}
~StyleFactory()
{
if( NULL != pBackgroundStyle )
{
delete pBackgroundStyle;
pBackgroundStyle = NULL;
}
}
//提供一个和风格类相同的方法,用于显示具体风格
void DisplayStyle()
{
pBackgroundStyle = CreateBackGroundStyle();
pBackgroundStyle->DisplayStyle();
}
//工厂方法,具体背景风格的创建过程由子类完成
virtual BackgroundStyle * CreateBackGroundStyle() = 0;
};
//古典风格工厂
class ClassicalStyleFactory : public StyleFactory
{
public:
BackgroundStyle * CreateBackGroundStyle()
{
BackgroundStyle * pClassicalStyle = new ClassicalStyle();
return pClassicalStyle;
}
};
//潮流风格工厂
class FashionStyleFactory : public StyleFactory
{
public:
BackgroundStyle * CreateBackGroundStyle()
{
BackgroundStyle * pFashionStyle = new FashionStyle();
return pFashionStyle;
}
};
//艺术风格工厂
class ArtStyleFactory : public StyleFactory
{
public:
BackgroundStyle * CreateBackGroundStyle()
{
BackgroundStyle * pArtStyle = new ArtStyle();
return pArtStyle;
}
};
#endif
StyleFactory背景风格抽象工厂中,提供了一个和背景风格类中一模一样的业务方法DisplayStyle(),该方法内部将调用工厂方法创建具体背景风格,并调用具体背景风格对象的DisplayStyle()方法。通常设计一个类的时候,需要考虑到单一原则,也就是这个类只实现一个业务功能。为了实现这个业务功能,把一些公用的业务方法放到抽象类中,子类就可以复用这个业务方法了。本例中,各子类工厂就能复用抽象工厂StyleFactory中的DisplayStyle()方法。同时在父类中定义一些虚业务方法,这些业务方法由子类具体实现。本例中,抽象工厂StyleFactory中的CreateBackGroundStyle是一个虚业务方法,为了实现创建背景风格的业务功能,由子类实现该业务方法,创建具体的背景风格。
测试实现代码如下:
#include <iostream>
#include "StyleFactory.h"
using namespace std;
int main()
{
/*********************创建古典风格背景***************************/
StyleFactory * pClassicalStyleFactory = new ClassicalStyleFactory();
pClassicalStyleFactory->DisplayStyle();
delete pClassicalStyleFactory;
pClassicalStyleFactory = NULL;
/*********************创建潮流风格工厂*************************/
StyleFactory * pFashionStyleFactory = new FashionStyleFactory();
pFashionStyleFactory->DisplayStyle();
delete pFashionStyleFactory;
pFashionStyleFactory = NULL;
/*********************创建艺术风格工厂*************************/
StyleFactory * pArtStyleFactory = new ArtStyleFactory();
pArtStyleFactory->DisplayStyle();
delete pArtStyleFactory;
pArtStyleFactory = NULL;
return 0;
}
3.3 编译执行结果
编译并执行,结果如下:
通过将业务方法的调用移入工厂类,可以直接使用工厂对象来调用产品对象的业务方法,客户端无须直接使用工厂方法,工厂方法被隐藏了。在某些情况下我们也可以使用这种设计方案。
4、工厂方法模式总结
工厂方法模式是简单工厂模式的延伸,它继承了简单工厂模式的优点,同时还弥补了简单工厂模式的不足。工厂方法模式是使用频率最高的设计模式之一,是对象创建型模式。
4.1. 主要优点
工厂方法模式的主要优点如下:
(1) 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。也就是说工厂方法模式封装了变化,封装了对象创建的具体细节,符合"封装变化原则"。
(2) 每一个具体工厂只负责创建一个对应的产品,符合"单一原则"。
(3) 使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了,这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”。
(4)客户端只需要认识抽象的产品类,无需认识具体产品类,降低了客户端和具体产品类的耦合度。也就是说客户端针对接口进行编程,符合"针对接口进行编程而不是针对具体进行编程原则"。
4.2. 主要缺点
工厂方法模式的主要缺点如下:
(1) 在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,系统中类的个数将成对增加,在一定程度上增加了系统的复杂度,有更多的类需要编译和运行,会给系统带来一些额外的开销。
3. 工厂方法模式具体应用
(1)通常在使用word办公软件的时候,会根据需要绘制出饼状图,柱状图,折线图等图形。可以提供一个抽象工厂类和三个工厂子类,创建具体图形。
(2)QQ空间背景样式,博客背景样式等都提供了各种风格的样式。可以提供一个抽象工厂类和三个工厂子类创建出各个不同的背景风格,用来装饰QQ空间。
(3)网页下载工具的开发: 根据需要可以下载新浪网页、腾讯网页、搜狐网页等。创建不同的网页工厂对象,下载对应类型的网页内容。
(4)淘宝购物最后一个支付环节,可以选择货到付款、网上银行、支付宝等类型支付。用户可以选择具体的支付方式完成订单,这也是工厂方法模式的一种应用。
(5)电影院打折算法: VIP5折、学生票5折、成人票正常收费等打折算法。
(6)多功能计算器的开发:封装加减乘除等运算操作(大话设计模式的例子)
(7)在很多游戏场合,游戏角色可以选择各种各样的武器,如:手枪、AK47、步枪、大刀等。
(8)如果电脑上装有QQ输入法、搜狗输入法、微软拼音输入法,用户可以设置使用哪种类型的输入法。类似的还可以设置IE浏览器、谷歌浏览器、火狐浏览器。可以设置word2003或者金山的WPS。这些都可以理解为工厂方法模式的一种运用。
(9)软件公司决策是否开发哪一种产品,银行卡识别、身份证识别还是驾驶证识别。
(10)开发火车票图像识别软件(OCR),对识别的结果可以保存为txt、word、pdf等格式。
(11)开发一套多种类型图片预览软件,可以读取jpg,bmp,gif等格式图片。
(11)生活中也有很多类似的工厂: 富士康代工工厂;安踏加工厂;咖啡生产基地;沃尔玛等超市提供各种产品供用户使用;肯德基马当劳等。