Command模式,Functor与对之应用的一些想法

来源:互联网 发布:matlab r2017a mac 编辑:程序博客网 时间:2024/05/20 16:45

command模式:以对象封装命令;避免了以其他方式传递命令易错的特点(如,使用string)
最简单的command命令模式是:接受某具体command,调用相应的receiver进行处理,所进行的不过是转调。
最基本的改进是:设定某些条件,在改条件成立时,invoker才进行调用。如此可以实现:每隔多少时间进行某些操作的方法。
也可以在调用前进行某些逻辑,这就是:主动式命令(相反称之为转发式命令)
最麻烦的是,要手工编写很多的具体命令。loki中使用Functor来帮助实现。
另有:多动作之序列化,借此可实现,在某条件成立时,连续按序执行一系列特地命令。且这些命令可以单独使用。是不是以一个汇总的单独command既可以实现?

原来换肤是用命令模式来实现的?……

使用ResultType来定义functor的返回值以实现template出一系列functors,很有意思啊

 

在实作Functor中,我们会希望对用户来说,无论其定义多少个命令的functor,他所看到的就只有一个接口。

 

使用Typelist来实现不定参数。

其中,TList是个Typelist,对Typelist的解析在FunctorImpl中,FunctorImpl解析出实际的参数(使不定参数定值化),而将这些定值化的参数定义于本地。

FunctorImpl并不如我所想定义了一堆的Typedef(实际上确实是通过定义了一堆typedef来实现的,但不是在FunctorImpl中)

重点注意:: public Private::FunctorImplHelper 这句,这实际上是继承了一个FunctorImplHelper,并以Typelist的长度(即实际的元素个数)为参数的template.Length定义于Typelist中。

比如,用户如此使用:

于是这里的TL::Length<TList>::value == 2,于是模板被特化到FunctorImplHelper<2>中去。

在Loki中,限定Type的个数最多只能是15。于是可以想到FunctorImplHelper至少有15个。

例如,FunctorImplHelper<2>如下:

使用了定义于 Typelist中的TypeAt来Index该2个元素的Typelist中的所有元素。但由于该TypeAt出来的仅仅是用户所给定的原始Type,所以有了使用TypeTraits的进一步处理。相应的,在每个FunctorImplHelper中都定义了其相应的operator() (由参数个数的不同,而定义了正确的operator() ,因为定义一个operator()(...)并不是一个好注意…… )

 

完整的FunctorImplHelper定义如下:

 

在FunctorImpl中,对FunctorImplHelper的使用颇有意思。使FunctorImpl实作出FunctorImplHelper 的仅仅是: public Private::FunctorImplHelper,仅仅是这个继承而已,而完成对Parm的定义则是通过: public Private::FunctorImplHelper后的::template In<R, TList, ThreadingModel>来完成的,Parm是通过对template In的继承而来的。

因此,在class Functor中可以如下定义class Functor将使用到的各型别:

 

以上实际上进行的是:class组合+模板偏特化。

 

Functor使用IMPL手法来实现相应的具体操作。

spImpl_ 是一个Private变量,因此对之访问仅限于class内部。

spImpl_定义为:std::auto_ptr<Impl> spImpl_; 在使用中,将指向具体的FunctorImpl<R, TList, ThreadingModel>

于是,在class Functor内,我们可以很方便的写出以下代码:

 

以上是构造函数。可以看到,最终使用了struct Helper的构造函数。

注意:spImpl_是一个指向具体的FunctorImpl<R, TList, ThreadingModel>的智能指针,R是用户给定的。而FunctorImpl中,由FunctorImplHelper重载了operator(),FunctorImpl将之继承至了自己的public,因此,对这些重载了operator()的class,我们可以直接调用之,这就是仿函数。当我们直接调用这些对象时,编译器会适当的进行这些对象所重载的operator()的调用。

如此,一路继承到class Functor中后,我们就可以写出这样的调用代码:

 

*spImpl_ 会调用到struct Helper中的operator*,该operator*返回一个指向具体FunctorImpl的智能指针,该FunctorImpl是一个仿函数,于是自然调用到定义于FunctorImpl中的,已具体化参数的operator()

但实际上,C++不允许同名,但参数个数不同的templates,因此这么多的operator()是错误的。但实际这些是能编译通过的,因为在一次使用中,我们最终会选定某个operator(),因此只会使用到某个operator(),因此编译器只编译其中的一个operator()

 

接下来要进行的是处理仿函数。

 

首先,Functor本身是仿函数,因其定义了operator(),因此,Functor应该有一个构造函数,接受某仿函数完成构造。

如下:

 

ParentFunctor 的定义是:Wraps functors and pointers to functions,其实现为:

 

有没有觉得很神奇,突然出现了一个class ParentFuncto,且在实作中使用了typedef typename ParentFunctor::Impl Base;及一系列关于Base::Parm的typedefs。但请注意,ParentFunctor仅仅是template 的一个参数 class ParentFunctor,即是,在使用FunctorHandler时由用户传递的参数。而我们在使用FunctorHandler时,是这样的:

 

因此,这里的 ParentFuncto实际上是:Functor,而Functor 是class Functor自身。这里的整个构造是:FunctorHandler中的FunctorHandler(const Fun& fun) : f_(fun) {}完成的。

这里的意思是:“以仿函数Fun之对象为参数的Functor构造函数”,只不过这里的Functor是其自身而已。

实际上,这里所要完成的是:以某一个用户给定的仿函数来完成Functor class的构造。Functor(Fun fun)中给定的Fun是typename,而fun,是具体的仿函数。于是,从Functor的该构造函数出发,通过new FunctorHandler<Functor, Fun>(fun) 实作出了一系列的operator(),这些operator()以确定的参数,调用用户所给仿函数中所重载的operator()。

这就是故事的全部。

 

在这之前的:

 使用的是:emplate<typename R, class TList = NullType,
        template<class> class ThreadingModel = DEFAULT_THREADING>

这用于class template Funtor的构造。而现在的template <typename Fun>则作为构造函数的参数。

这里所做的事是:构造函数的函数本体对spImpl_成员进行初始化,使之指向一个型别为FunctorHandler的新对象,该对象系通过适当参数被具现化和初始化的。

可以看到spImpl_的定义是:Helper spImpl_; 因此这里在以所new的FunctorHandler指针来构造Helper,构造Helper所使用的函数是:

对此,我们可以写个测试程序如下:

 

测试1.

1.定义了一个仿函数(重载了operator())

2.定义template Functor实例 cmd。其中使用了上述的构造函数。

3.调用实例cmd的operator(),该operator()调用会自动调用至TestFunctor中的operator()

 

在测试1中,我们显示的定义了一个TestFunctor实例,再用它来实例化template functor。

也可以直接使用一个函数进行,如下:

 

测试2:

但,如果出现了TestFunction的重载版本,那么编译器会报错。解决办法可以是:定义函数指针并使用之;在使用时进行型别转换。具体不再详述。

 

可能比较麻烦的是成员函数指针。实际上,成员函数指针并不常用。

在Loki中,提供了MemFunHandler指向成员函数。

 

其定义如下:

在Functor中存在对其调用:

PtrObj是外部传来的某对象实例的指针,memFn是外部来的某成员函数的指针。(别忘了,对对象成员的函数的函数指针形式调用需要同时使用对象实例和函数指针。)

因此,在MemFunHandler中,以下代码就不难理解了。

无非是typedef 参数,转调成员函数等动作。

这里template参数中typename PointerToObj, 是一个指向对象的指针,之所以不直接使用typename Obj,是因为需要更大的泛用。

 

自己写完后,好好的反思了下。写了这么多,只不过是在剖析别人的源代码而已。这样的技术很炫,但这似乎也只是语言技术上的炫目。对一个软件开发人员来说,什么才是最重要的?我想绝不是也不应该是语言的炫目技术。

似乎自己一直以来都太沉迷于语言细节,仿佛什么东西最晦涩,那搞懂什么就越有成就感……

 

我们应该更关注什么?

原创粉丝点击