定义
工厂方法模式(Factory Method Pattern)又称为工厂模式,也叫虚拟构造器(Virtual Constructor)模式或者多态工厂(Polymorphic Factory)模式,它属于类创建型模式。
在工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。
模式结构图
工厂方法模式角色
- Product:抽象产品,工厂方法模式所创建的对象的超类,也就是所有产品类的共同父类或共同拥有的接口。在实际的系统中,这个角色也常常使用抽象类实现。
- ConcreteProduct:具体产品,这个角色实现了抽象产品(Product)所声明的接口,工厂方法模式所创建的每一个对象都是某个具体产品的实例。
- Factory:抽象工厂,担任这个角色的是工厂方法模式的核心,任何在模式中创建对象的工厂类必须实现这个接口。在实际的系统中,这个角色也常常使用抽象类实现。
- ConcreteFactory:具体工厂,担任这个角色的是实现了抽象工厂接口的具体类。具体工厂角色含有与业务密切相关的逻辑,并且受到使用者的调用以创建具体产品对象。
时序图
①先调用具体工厂对象中的方法createProduct()
②根据传入产品类型参数(也可以无参),获得具体的产品对象
③返回产品对象并使用
c++代码示例
#include <iostream>
using namespace std;
//抽象产品
class Fruit
{
public:
virtual void printInfo() = 0; //将printInfo 声明为纯虚函数,拥有纯虚函数的类是抽象基类
};
//具体产品1
class Apple : public Fruit //共有继承抽象基类
{
public:
virtual void printInfo() { cout << "Apple::printInfo()\n"; }
};
//具体产品2
class Banana : public Fruit
{
public:
virtual void printInfo() { cout << "Banana::printInfo()\n"; }
};
/*
补充知识:
1.父类的static变量和函数在派生类中依然可用,但是受访问性控制(比如,父类的private域中的就不可访问),
而且对static变量来说,派生类和父类中的static变量是共用空间的,这点在利用static变量进行引用计数的时候要特别注意。
2.static函数没有“虚函数”一说。因为static函数实际上是“加上了访问控制的全局函数”,全局函数哪来的什么虚函数?
3.派生类的friend函数可以访问派生类本身的一切变量,包括从父类继承下来的protected域中的变量。但是对父类来说,他并不是friend 的。
*/
//抽象工厂
class FruitFactory
{
public:
virtual Fruit* createFruit() = 0; //声明为纯虚函数,则FruitFactory类为抽象基类
};
//具体工厂1:专门生产苹果的工厂
class AppleFactory
{
public:
//这里不在加virtual 是因为静态函数不能声明为虚函数。为什么用static?因为静态方法属于类方法,生存期大于实例对象,因此可以通过类名::方法名来调用而无需创建实例对象了。
static Fruit* createFruit()
{
//因为苹果工厂只生产苹果,直接return Apple实例即可,无需判断像简单工厂中的参数(实际上这里也没有参数)
return new Apple();
}
};
//具体工厂2:专门生产香蕉的工厂
class BananaFactory
{
public:
static Fruit* createFruit()
{
return new Banana();
}
};
int main()
{
//工厂模式之工厂方法模式
//通过苹果工厂得到苹果(实例对象),无需传入参数,也无需知道实现细节, 甚至也无需创建实例工厂角色。
Fruit* apple = AppleFactory::createFruit();
apple->printInfo();
同理通过香蕉工厂得到香蕉
Fruit* banana = BananaFactory::createFruit();
banana->printInfo();
return 0;
}
测试结果
工厂方法模式的优缺点
优点:
①在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节,用户只需要关心所需产品对应的工厂,无须关心创建细节,甚至无须知道具体产品类的类名。
②基于工厂角色和产品角色的多态性设计是工厂方法模式的关键。它能够使工厂可以自主确定创建何种产品对象,而如何创建这个对象的细节则完全封装在具体工厂内部。工厂方法模式之所以又被称为多态工厂模式,是因为所有的具体工厂类都具有同一抽象父类。
③使用工厂方法模式的另一个优点是在系统中加入新产品时,无须修改抽象工厂和抽象产品提供的接口,无须修改客户端,也无须修改其他的具体工厂和具体产品,而只要添加一个具体工厂和具体产品就可以了。这样,系统的可扩展性也就变得非常好,完全符合“开闭原则”,这点比简单工厂模式更优秀。
缺点:
①在添加新产品时,需要编写新的具体产品类,而且还要提供与之对应的具体工厂类,****,有更多的类需要编译和运行,会给系统带来一些额外的开销。
②由于考虑到系统的可扩展性,需要引入抽象层,在客户端代码中均使用抽象层进行定义,增加了系统的抽象性和理解难度,增加了系统的实现难度。
适用场景
在以下情况下可以使用工厂方法模式:
①一个类不知道它所需要的对象的类:在工厂方法模式中,客户端不需要知道具体产品类的类名,只需要知道所对应的工厂即可,具体的产品对象由具体工厂类创建;客户端需要知道创建具体产品的工厂类。
②一个类通过其子类来指定创建哪个对象:在工厂方法模式中,对于抽象工厂类只需要提供一个创建产品的接口,而由其子类来确定具体要创建的对象,利用面向对象的多态性和里氏代换原则,在程序运行时,子类对象将覆盖父类对象,从而使得系统更容易扩展。
③将创建对象的任务委托给多个工厂子类中的某一个,客户端在使用时可以无须关心是哪一个工厂子类创建产品子类,需要时再动态指定,可将具体工厂类的类名存储在配置文件或数据库中。