1. 说明
关于设计模式的说明和解释主要是学习的《设计模式 可复用面向对象软件的基础》一书,代码示例则主要是来自于设计模式|菜鸟教程网站,将该网站中原有的java示例改写成c++基础的代码,用以加深自己的理解,方便学习之用。看官们也可直接参考这两个来源。
2. 工厂模式说明
什么是工厂模式
工厂模式是创建型模式的一种,它通过一个抽象的通用接口屏蔽了具体类的实现细节,这样在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。
意图
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂模式使一个类的实例化延迟到其子类。
适用的场合
当一个类不知道它所必须创建的对象的类的时候;
当一个类希望由它的子类来指定它所创建的对象的时候;
当类将创建对象的职责委托给多个子类中的某一个,并且你希望将哪一个子类是代理者这一信息局部化的时候。
优点
屏蔽了多个子类的实现细节,要创建一个类时只要调用同一个接口类就行了;
可扩展性好,当需要增加某一类产品时,只要增加一个工厂类就可以了;
缺点
增加一个产品时要分别增加具体的产品类和一个工厂接口,这样就增加了更多的工作内容。
3. 示例说明
3.1 框架
纸上得来终觉浅,绝知此事要躬行。光看文字说明反正我是觉得云里雾里的,下面就用一个具体的实例来说明一下工厂模式的简单实现。这个示例中我们将定义两个工厂分别实现Color和Shape类和成员函数。程序框架如下图:
3.2 代码实现
基类
Shape.h
#ifndef __SHAPE__
#define __SHAPE__
class Shape{
public:
virtual void draw() = 0;
};
#endif
class Shape是所有具体形状(Rectangle, Square, Circle)的基类,只包含一个没有实现的虚函数draw(),该函数在每一个具体类中分别有不同的实现。
它是提供给上层的接口,即上一层调用不同类的函数时都是通过这个类的接口调用的。
产品类的实现
Rectangle.h
#ifndef __RECTANGLE__
#define __RECTANGLE__
#include <iostream>
#include "Shape.h"
class Rectangle : public Shape{
public:
Rectangle(){};
void draw() {std::cout << "Inside Rectangle::draw()." << std::endl;};
};
#endif
Square.h
#ifndef __SQUARE__
#define __SQUARE__
#include <iostream>
#include "Shape.h"
class Square : public Shape{
public:
Square(){};
void draw() {std::cout << "Inside Square::draw()." << std::endl;}
};
#endif
Circle.h
#ifndef __CIRCLE__
#define __CIRCLE__
#include <iostream>
#include "Shape.h"
class Circle : public Shape{
public:
Circle(){};
void draw() {std::cout << "Inside Circle::draw()" << std::endl;};
};
#endif
Rectangle/Square/Circle是继承自Shape类的三个具体的实现,它们分别实现了draw()函数。在实际调用时也是分别进入到这三个类里面调用draw()。
工厂类的实现
Factory.h
#ifndef __FACTORY__
#define __FACTORY__
#include <string>
#include "Shape.h"
#include "Color.h"
using namespace std;
class ShapeFactory {
public:
Shape *GetShape(string shapeType);
};
class ColorFactory {
public:
Color *GetColor(string color);
};
#endif
Factory.cpp
#include <cstddef>
#include <string>
#include "Circle.h"
#include "Rectangle.h"
#include "Square.h"
#include "Red.h"
#include "Green.h"
#include "Blue.h"
#include "Factory.h"
Shape *ShapeFactory::GetShape(string shapeType)
{
string str1 = "Circle";
string str2 = "Rectangle";
string str3 = "Square";
if (shapeType == str1){
return new Circle();
}else if (shapeType == str2){
return new Rectangle();
}else if (shapeType == str3){
return new Square();
}
return NULL;
}
Color *ColorFactory::GetColor(string color)
{
string str1 = "Red";
string str2 = "Green";
string str3 = "Blue";
if (color == str1){
return new Red();
}else if (color == str2){
return new Green();
}else if (color == str3){
return new Blue();
}
return NULL;
}
这里实现了Shape的工厂类,该类只包含一个函数GetShape(),根据输入的参数不同分别返回不同的形状类(Rectangle/Square/Circle)的实例,但是这些实例的类型都是Shape *。这样做的好处是在应用层调用时不必再关心具体类的具体实现,只要用同一个函数得到同一种类型的实例,再通过这个实例去调用同样的成员函数。
Color工厂类的实现差不多,这里是用于对比说明。
Color类相关代码
Color.h
#ifndef __COLOR__
#define __COLOR__
class Color {
public:
virtual void fill() = 0;
};
#endif
Red.h
#ifndef __RED__
#define __RED__
#include <iostream>
#include "Color.h"
class Red : public Color{
public:
Red(){};
void fill() {std::cout << "Inside Red::fill()" << std::endl;};
};
#endif
Blue.h
#ifndef __BLUE__
#define __BLUE__
#include <iostream>
#include "Color.h"
class Blue : public Color{
public:
Blue(){};
void fill() {std::cout << "Inside Blue::fill()" << std::endl;}
};
#endif
Green.h
#ifndef __GREEN__
#define __GREEN__
#include <iostream>
#include "Color.h"
class Green : public Color{
public:
Green(){};
void fill() {std::cout << "Inside Green::fill()" << std::endl;};
};
#endif
应用程序代码
demo.cpp
#include "Factory.h"
int main(int argc, char **argv)
{
ShapeFactory sf;
Shape *s1 = sf.GetShape("Rectangle");
s1->draw();
Shape *s2 = sf.GetShape("Square");
s2->draw();
Shape *s3 = sf.GetShape("Circle");
s3->draw();
ColorFactory cf;
Color *c1 = cf.GetColor("Red");
c1->fill();
Color *c2 = cf.GetColor("Blue");
c2->fill();
Color *c3 = cf.GetColor("Green");
c3->fill();
return 0;
}
demo首先生成一个工厂类的实例sf,调用sf的GetShape()函数生成不同的Shape实例,分别调用它们的draw()函数。注意,这里虽然是不同的Shape实例,但是它们都是Shape *类型的,也就说都是按照同样的接口调用的。
编译
g++ *.cpp -o demo
运行
$ ./demo
Inside Rectangle::draw().
Inside Square::draw().
Inside Circle::draw()
Inside Red::fill()
Inside Blue::fill()
Inside Green::fill()
4. 分析总结
工厂模式的特点在于:
- 定义的基类要抽象出所有的具体子类的功能函数,以便使用统一的接口调用,如这里的draw();
- 如果某些子类拥有独有的功能函数,那么其他相关的子类也应该要有该函数名称的实现,哪怕只是return NULL;
- 独有功能函数过多,会额外增加大量无效代码;
- 不同种类的产品,需要重新实现一套工厂类+基类+子类的;
参考资料
《设计模式 可复用面向对象软件的基础》
工厂模式——菜鸟教程