gnugk5.5源码分析(3)之模板工厂

前言

如果按前面第二节的内容,这节应该是接着讲Ras或者H225的socket收到包后,怎么去处理的事项。但是在开始讲如何数据包之前,我想先讲一下gnugk的模板工厂定义与实现。之所以要这样,是因为在接下来的很多地方都会用到这个实现,所以感觉应该先讲讲。
简单来说,gnugk的模板工厂定义,就是定义了四类工厂方法,不带参数、带1个参数、带2个参数、带3个参数的的工厂,这里讲到的工厂,是一种设计方法,工厂可以构造生成产品;而采用模板,可以更加灵活的定义工厂的类型。在gnugk中这个实现被定义在factory.h文件中。

一、函数构造器Functor系列

template<typename R>
struct Functor {
	typedef R result_type;
	virtual ~Functor() { }
};

从上可以看到最基础抽象的函数构造器Functor,模板接受一个参数类型R作为返回类型,并且只定义了一个虚析构~Functor。接下来看下它的子类。

template<typename R>
struct Functor0 : public Functor<R> {
	virtual R operator()() const = 0;
};

template<typename R, typename T1>
struct Functor1 : public Functor<R> {
	typedef T1 argument_type;
	virtual R operator()(T1) const = 0;
};

template<typename R, typename T1, typename T2>
struct Functor2 : public Functor<R> {
	typedef T1 first_argument_type;
	typedef T2 second_argument_type;
	virtual R operator()(T1, T2) const = 0;
};

template<typename R, typename T1, typename T2, typename T3>
struct Functor3 : public Functor<R> {
	typedef T1 first_argument_type;
	typedef T2 second_argument_type;
	typedef T3 third_argument_type;
	virtual R operator()(T1, T2, T3) const = 0;
};

可以看到有四个子类继承自Functor,分别是Functor0、Functor1、Functor2、Functor3;这里的所谓的0,1,2,3的命名,也是指示了每个构造器可接受的参数的个数。我们以Functor0为例来说明下。

template<typename R>
struct Functor0 : public Functor<R> {
	virtual R operator()() const = 0;
};

Functor0继承自Functor,也是只接受一个参数类型R,作为返回值,但是Functor0新添加定义了一个纯虚函数,virtual R operator()() const = 0,实际上是一个实例调用运算符();这个操作符的使用场景是,假设现有一个类A,实例化A a; 当调用a()时,即调用到了这个操作符;
综上,Functor0的行为是接受一个参数类型R,当返回值;并协定其子类需要重载实现调用运算符,且这个调用运算符是无参的。

采用同样的方法,可以看出Functor1、Functor2、Functor3相对于Functor0,只是协议这个调用运算符,分别需要接受1个、2个、3个参数而已。

二、模板工厂类Factory

这里先整体说明一下这个模板工厂类Factory的实现功能:
1 接受两个参数类型,第一个是类类型,作为产品Product,第二个是这个产品的标识Identifier,默认类型为const char *;
2 定义Creator0、Creator1、Creator2、Creator3分别对应于Functor0、Functor1、Functor2、Functor3,支持无参,带1个参数、带2个参数、带3个参数的产品构造器;
3 每个类型的产品构造器都在模板特化时,注册保存相对应的构造器,以便于后续在调用Create的方法时,能正确查找并构造相应的产品实例。

接下来,我们一部分一部分来查看代码实现;

2.1 模板工厂类的基础定义

# 接受两个参数类型,第一个约定作为产品类型(Product),第二个约定为该产品的标识类型(Identifier)且默认为const char*类型
template<class Product, typename Identifier = const char *>
class Factory {
public:

	# 重命名定义内部使用的类型名,这里需要重点记住Creator和Associations的定义

	typedef Factory<Product, Identifier> Self;
	# 定义Creator,即产品构造器,从前面第一点,我们知道Functor的作用是接受一个参数类型当返回类型;
 	# 所以这个Creator的概念,其实本质上就是构造创建并返回一个Product实例的指针。
	typedef Functor<Product *> *Creator;
	# 内部维护的映射表,即产品标识对应着哪一个产品构造器;就是哪一个产品标识能构造返回相对应的哪类产品。
	typedef std::map<Identifier, Creator> Associations;

	# 静态函数,注册产品标识和产品构造器,保存对应关系在m_associations成员中
	static Creator Register(Identifier n, Creator c);
	static void SetDefaultCreator(Creator c) { m_default = c; }

	// registrars
	# 注册器,用于后面的Creator0、Creator1等;这个类本质上就是调用到上面的Register静态函数,但是为什么还要定义这个类呢?
	# 原因是这里所谓的注册记录功能,是维护在Factory类内部的,而Creator0、Creator1,这些的构造器函数,是可以被上层调用者继承的,那么就不法保证上层一定会主动调用Factory的Register静态函数,则内部的这个注册逻辑就乱了,所以这里定义Registrar类,而Creator0等继承Registrar类,从而保证内部维护的注册逻辑关系是一定存在的。
	class Registrar {
	protected:
		Identifier m_id;
		Creator m_old;
		Registrar(Identifier n, Creator c);
		~Registrar();
	};
private:
	static Associations *m_associations;
	static Creator m_default;
}

2.2 Factory的CreatorX系列构造器

可以看到每个构造器,只是所支持的参数个数不同而已。

	struct Creator0 : public Functor0<Product *>, Registrar {
		Creator0(Identifier n) : Registrar(n, this) { }
	};

	template<typename P1>
	struct Creator1 : public Functor1<Product *, P1>, Registrar {
		Creator1(Identifier n) : Registrar(n, this) { }
	};

	template<typename P1, typename P2>
	struct Creator2 : public Functor2<Product *, P1, P2>, Registrar {
		Creator2(Identifier n) : Registrar(n, this) { }
	};

	template<typename P1, typename P2, typename P3>
	struct Creator3 : public Functor3<Product *, P1, P2, P3>, Registrar {
		Creator3(Identifier n) : Registrar(n, this) { }
	};

这次,我们拿Creator1来举例说明。

	template<typename P1>
	struct Creator1 : public Functor1<Product *, P1>, Registrar {
		Creator1(Identifier n) : Registrar(n, this) { }
	};

从定义上可以看到Creator1继承自Functor1、Registrar;
Functor1<Product *, P1>的作用前面讲了,就是接受两个参数类型,第一个参数类型作为返回值类型,也就是产品Product,第二个参数就是构造这个产品时,重载实现实例调用运算符时需要传递的参数类型P1;
继承Registrar,前面已经说了,这是为了强制实现内部维护的产品标识与产品构造器的映射关系。
来继续看下Creator1的构造函数,Creator1(Identifier n) : Registrar(n, this) { };逻辑很简单,向上构造Registrar实例,其中这里的this指的是Creator1;从而下面的Registrar的构造函数可以看到,只是转化为调用Factory的Register静态方法;

template<class Product, typename Identifier>
Factory<Product, Identifier>::Registrar::Registrar(Identifier n, Creator c) : m_id(n)
{
#if defined(_WIN32) && (_MSC_VER > 1300)
	m_old = Register(n, c);
#else
	m_old = Self::Register(n, c);
#endif
}

来继续看下Factory的Register静态方法

template<class Product, typename Identifier>
Functor<Product *> *Factory<Product, Identifier>::Register(Identifier n, Creator c)
{
	static Associations associations;
	m_associations = &associations;
	Creator & d = associations[n];
	Creator old = d;
	d = c;
	return old;
}

这里的实现也是比较简单,就是接受两个参数,第一个参数是产品标识Identifier n,第二个参数是产品构造器Creator c;在这个例子中,c是指Creator1实例,Creator1是Creator的子类,所以向上转化传参肯定是没有问题的。
这里逻辑就很简单了,从映射表m_associations中查找,若找到就把旧的构造器保存在Registrar的m_old成员变量上,然后更新新的构造器c;这样产品标识n就和产品构造器c关联起来了。

其它的CreatorX,实现逻辑是一样的,只是定义时,可接受的参数个数不同而已。

2.3 Create构造方法

前面2.2讲的是构造器类型的定义,而真正被上层调用,构造生成相对应产品的,是这里的Create静态方法,同前面一样,它们的区别只是参数个数不同。

	static Product *Create(Identifier n)
	{
		Functor0<Product *> *f0;
		return FindCreator(n, 0, f0) ? (*f0)() : NULL;
	}

	template<typename P1>
	static Product *Create(Identifier n, P1 p1)
	{
		Functor1<Product *, P1> *f1;
		return FindCreator(n, 1, f1) ? (*f1)(p1) : NULL;
	}

	template<typename P1, typename P2>
	static Product *Create(Identifier n, P1 p1, P2 p2)
	{
		Functor2<Product *, P1, P2> *f2;
		return FindCreator(n, 2, f2) ? (*f2)(p1, p2) : NULL;
	}

	template<typename P1, typename P2, typename P3>
	static Product *Create(Identifier n, P1 p1, P2 p2, P3 p3)
	{
		Functor3<Product *, P1, P2, P3> *f3;
		return FindCreator(n, 3, f3) ? (*f3)(p1, p2, p3) : NULL;
	}

我们还是以接受一个参数的Create为例来说明下。

	template<typename P1>
	static Product *Create(Identifier n, P1 p1)
	{
		Functor1<Product *, P1> *f1;
		return FindCreator(n, 1, f1) ? (*f1)(p1) : NULL;
	}

需要调用这个Create方法时,要指明参数类型P1,同时传递两个参数。因为是需要接受一个参数的调用,所以所对应的构造器自然就是Functor1;这里就两步逻辑:
第1步:根据产品标识n,通过FindCreator方法,找到构造器保存在f1;
第2步:调用构造器实例(*f1)(p1),返回产品Product;

这里FindCreator方法,本质上就是进行map的查找而已。自行看下代码即可。

三、用法

在factory.h的开关的注释也举例了使用说明。这里简单记录说明一下。

第一步:定义函数,依据参数个数的需求,继承自CreatorX系列的某个构造器;
一般来说,在面各对象编程中,这个函数更多的是放置在类中。
如这里的SampleBase的Init方法,这里Init本身又是一个模板,这个模板定义不是必需的。

class SampleBase {
public:
	SampleBase() { }
	SampleBase(const char *n) { cerr << "This is a " << n << "\n"; }
	virtual ~SampleBase() { }

	template<class Derived>
	struct Init : public Factory<SampleBase>::Creator0 {
		Init(const char *n) : Factory<SampleBase>::Creator0(n), n_(n) { }
		virtual SampleBase *operator()() const { return new Derived(n_); }

		const char *n_;
	};
};

class SampleA : public SampleBase {
public:
	SampleA(const char *n) : SampleBase(n) { }
};

第二步:向Factory工厂注册产品
如这里,就是向Factory注册产品标识为字符串"SampleA",相对应的产品是SampleBase类实例,在这里,实际上的产品是指SampleA,之所以会是SampleA,是因为SampleBase::Init重载实现的operator()操作符,具体实例化的是new Derived(n_),即其的子类;在这个调用(SampleA::Init)中指的是SampleA,所以最终返回的是SampleA实例。

SampleA::Init<SampleA> SampleAInit("SampleA");

第三步:其它地方调用实例化SampleA
因为在第二步时,已经向Factory注册过了,所以在其它地方是可以直接通过产品标识"SampleA",来创建得到相应的产品的。

SampleBase *pa = Factory<SampleBase>::Create("SampleA");
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ava实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),可运行高分资源 Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现的毕业设计&&课程设计(包含运行文档+数据库+前后端代码),Java实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值