设计模式:工厂模式

工厂模式的意图是将生成对象的代码集中到一个地方,以方便统一管理,最简单的工厂模式是由基类负责生成各种派生类的对象,举个简单例子如下:

class Base {
public:
	static Base* create(const string &str);
};

class A : public Base {};

class B : public Base {};

Base* Base::create(const string &str) {
	if(str == "A") return new A();
	if(str == "B") return new B();

	return NULL;
}

这样就可以通过Base::create来创建A或B的对象,注意到create定义成Base的静态函数即可,但是就上面的例子而言,我们依然可以绕过Base::create而创建A或B的对象,即通过A、B的默认构造函数,所以更加严谨的做法可能是将A、B的默认构造函数(拷贝构造函数)定义为private,如下:

class Base {
public:
	static Base* create(const string &str);
};

class A : public Base {
	A() {}
	A(const A&) {}
	friend Base* Base::create(const string&);
};

class B : public Base {
	B() {}
	B(const B&) {}
	friend Base* Base::create(const string&);
};

Base* Base::create(const string &str) {
	if(str == "A") return new A();
	if(str == "B") return new B();

	return NULL;
}

注意到A、B的默认构造函数(及拷贝构造函数)被设置成了private,这是为了避免用户通过A、B的构造函数直接生成各自的对象,换言之,强迫用户将生成A、B对象的任务交给Base::create,同时,为了在Base::create中调用A、B的构造函数,需要将Base::create设置为A、B的友元。在实际的使用中,如果我们想增加另外一个派生于Base的类型C,那么只需修改Base::create中的代码即可。

在上面这种最简单的工厂模式中,基类和派生类的耦合还是比较紧的,因为基类的create需要直接操作派生类的构造函数,事实上,我们可以避免基类直接操作派生类的构造函数,这就是所谓的多态工厂。在多态工厂的设计模式中,派生类A和B都有各自的专属工厂,这个专属工厂直接操作A或B的构造函数,基类有个大工厂,这个大工厂通过调用各派生类的专属工厂来生成各派生类的对象,看个简单例子:

class Base {};

class BaseFactory {
	static BaseFactory bf;
	static map<string, BaseFactory*> factories;
protected:
	BaseFactory(){}
	BaseFactory(int);
public:	
	virtual Base* create(){ return NULL; }
	static Base* createProduct(const string &str);
};

这是基类以及基类工厂,我们重点关注基类工厂:BaseFactory,这个工厂有一个map结构的数据成员:factories,factories用于保存各派生类的专属工厂,同时,基类工厂还有一个成员函数:createProduct,用于创建各派生类的对象,createProduct的定义如下:

Base* BaseFactory::createProduct(const string &str) {
	if(factories.find(str) != factories.end())
		return factories[str]->create();
}
所以在createProduct中通过使用actories中保存的各专属工厂来创建派生类对象,str可以是派生类的名字,用于标识创建哪个派生类的对象。再回到BaseFactory中,还有一个 成员函数create,这个函数将来会被派生类工厂重新定义,用以创建各派生类的对象,派生类及派生类工厂的定义如下:

class A : public Base {
	A() {}
	A(const A&) {}
	friend class BaseFactory;
	class Factory;
	friend class Factory;
	class Factory : public BaseFactory {
	public:
		Base* create() { return new A(); }
	};
};
派生类工厂是作为派生类的嵌套类进行定义的,且访问属性为private,所以派生类工厂不能在派生类以外被使用,但是由于BaseFactory被定义成了派生类的友元,所以派生类工厂可以被BaseFactory访问,派生类工厂继承于BaseFactory,唯一要做的就是重新定义create(),在create()将完成派生类对象的创建。由于create为虚函数,所以即使使用BaseFactory类型的指针也可以正确访问派生类工厂的create(只要该指针真正指向的是派生类工厂对象)。完整的代码如下:

#include<iostream>
#include<string>
#include<map>
using namespace std;

class Base {};

class BaseFactory {
	static BaseFactory bf;
	static map<string, BaseFactory*> factories;
protected:
	BaseFactory(){}
	BaseFactory(int);
public:	
	virtual Base* create()  { return NULL; } 
	static Base* createProduct(const string &str);
};

map<string, BaseFactory*> BaseFactory::factories;
BaseFactory BaseFactory::bf(1);


Base* BaseFactory::createProduct(const string &str) {
	if(factories.find(str) != factories.end())
		return factories[str]->create();
}

class A : public Base {
	A() {}
	A(const A&) {}
	friend class BaseFactory;
	class Factory;
	friend class Factory;
	class Factory : public BaseFactory {
	public:
		Base* create() { return new A(); }
	};
};

class B : public Base {
	B() {}
	B(const B&) {}
	friend class BaseFactory;
	class Factory;
	friend class Factory;
	class Factory : public BaseFactory {
	public:
		Base* create() { return new B(); }
	};
};

BaseFactory::BaseFactory(int) {
	BaseFactory::factories["A"] = new A::Factory;
	BaseFactory::factories["B"] = new B::Factory;
}

int main() { 
	Base *p = BaseFactory::createProduct("A");
	
	return 0; 
}
对上面的代码再做几点解释:

1、基类工厂BaseFactory中有一个自身的static数据成员: bf ,因为bf的定义将发生在main()之前,所以bf存在的意义是调用构造函数以初始化factories,这里bf调用了有参的构造函数;

2、基类工厂BaseFactory中有两个构造函数,一个是无参的默认构造函数,另一个是有int参数的构造函数,前者是用于被派生类工厂调用的(因为创建派生类工厂对象的时候需要调用其基类的默认构造函数),而后者只是为了在定义bf 的时使用,用于初始化factories;

上面的代码只是阐述了多态工厂的基本思想,仍然有很多可以改进的地方微笑,希望大家多提意见,总之,多态工厂就是使用派生类自己的工厂来创建派生类,所以基类就不需要了解太多派生类的细节,只要派生类向基类工厂注册了自己的专属工厂就行。






  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值