您的位置:首页 > 编程语言 > C语言/C++

Modern C++ design 第5章 Command 设计模式

2013-10-24 20:49 453 查看

Modern C++ design 第5章 Command 设计模式

命令模式将发出命令的责任和执行命令的责任分离开来

 

Command模式两个特点

接口分类;

时间分离。Command保存了一个整装待发的处理请求,供将来运用。Command模式中,收集“某处理动作所需环境”的时刻和执行该动作的时刻并不相同。

 

可以将Command分为两类

1.转发式命令forwarding command

2.主动式命令active command

 

C++中支持operator()的所有构件:

1. C-like function(C风格函数)

2. C-like pointer to function(C风格的函数指针)

3. reference to function,指向函数的引用

4. functor(仿函数),即自行定义了operator()的一种对象

5. operator.*或operator->*的施行结果。

6. construct call(构造函数)

设计思想,首先思考下对函数指针的封装,要做到通用,那就必须知道传入的函数指针的类型,应该要让编译器自动推导出来,而类模板是不允许推导的,只有函数可以进行参数推导,故在设计类的时候,可以在构造函数中使用模板推导,而要做到对operator()的封装,那必须传入返回值和参数,这两个模板参数,于是便有了如下的声明:

template <typename R = void, class TList = NullType>
class Functor
{

template <typename Fun>
Functor(Fun fun) {}// 此处通过函数模板自动推导功能,获得了传入的fun的型别Fun

};


然后里面的构造函数需要推导出类型,然后在传入后,需要将fun即函数指针保存下来,需要有个类对fun进行一个封装,此时需要传入R(返回值),TList(参数列表),Fun(指针类型),其中R和TList都是Functor的,通过Fun我们可以得到指针类型。
    基于以上的分析我们可以得到如下的代码:
template <typename R, class TList, typename Fun>
class FunctorHandler
{
public:
FunctorHandler(Fun const & fun ) : fun_(fun) { }
private:
Fun fun_;
};

template <typename R = void, class TList = NullType>
class Functor
{
public:
//typedef FunctorImpl<R,TList> Impl;
typedef FunctorImpl<R,TList,Fun> Impl;
typedef R ResultType;
typedef TList ParamList;
public:
//R operator() (P1);

template <typename Fun>
Functor(Fun fun) : spImpl_(new FunctorHandler<R,TList,Fun>(fun))
{ }
private:
std::auto_ptr<Impl> spImpl_;
};

但上面是有问题的,在想要new FunctorHandler的时候,返回一个指针,此时想要保存下这个指针,但是此时FunctorHandler的类型却无法表示出来,因为实例化的时候需要Fun类型,但是这个类型却是在构造函数的时候才推导出来的,所以在别处无法使用,此时我们就必须要在实例化的时候去掉Fun类型,这个怎么办呢?此时我们可以通过C++中的构造,声明一个抽象类,它没有具体的实现,只是提供一个一些接口,然后让子类去具体实现,根据以上的想法,我们就可以设计如下的代码:
// 基类
template <typename R, class TList>
class FunctorImpl;

// 无参数的实现
template <typename R>
class FunctorImpl<R, NullType>
{
public:
typedef R ResultType;
virtual R operator() () = 0;
virtual ~FunctorImpl() {}
};

template <class ParentFunctor, typename Fun>
class FunctorHandler : public ParentFunctor::Impl
{
typedef typename ParentFunctor::Impl Base;
public:
FunctorHandler( Fun const & fun ) : fun_(fun) { }
private:
Fun fun_;
};

template <typename R = void, class TList = NullType>
class Functor
{
public:
typedef FunctorImpl<R,TList> Impl;
//typedef FunctorImpl<R,TList,Fun> Impl;
typedef R ResultType;
typedef TList ParamList;
public:
//R operator() (P1);

template <typename Fun>
//Functor(Fun fun) : spImpl_(new FunctorHandler<R,TList,Fun>(fun))
Functor(Fun fun) : spImpl_(new FunctorHandler<Functor,Fun>(fun))
{ }

private:

std::auto_ptr<Impl> spImpl_;
};

上面的程序只要再实现下operator()操作,并且无参数情况下,就可以有个简单的实现了,下面是完整的实现代码,代码放在git中了。
    现在再设计使其支持一个参数,代码如下:
template <typename R, typename P1>
class FunctorImpl<R, LOKI_TYPELIST_1(P1)>
{
public:
typedef R ResultType;
typedef P1 Param1;
virtual ResultType operator() (Param1) = 0;
virtual ~FunctorImpl() {}
};

template <class ParentFunctor, typename Fun>
class FunctorHandler : public ParentFunctor::Impl
{
typedef typename ParentFunctor::Impl Base;
typedef typename Base::ResultType ResultType;
typedef typename Base::Param1 Param1;
public:
FunctorHandler( Fun const & fun ) : fun_(fun) { }
ResultType operator() () { return fun_(); }
ResultType operator() (Param1 p1) { return fun_(p1); }
private:
Fun fun_;
};
template <typename R = void, class TList = NullType>
class Functor
{
public:
typedef FunctorImpl<R,TList> Impl;
//typedef FunctorImpl<R,TList,Fun> Impl;
typedef R ResultType;
typedef TList ParamList;
typedef typename Impl::Param1 Param1;
public:
//R operator() (P1);

template <typename Fun>
//Functor(Fun fun) : spImpl_(new FunctorHandler<R,TList,Fun>(fun))
Functor(Fun fun) : spImpl_(new FunctorHandler<Functor,Fun>(fun))
{ }
ResultType operator() () { return (*spImpl_)(); }
ResultType operator() (Param1 p1) { return (*spImpl_)(p1); }

private:

std::auto_ptr<Impl> spImpl_;
};

用这种方法设计的时候,会带来问题,无法实例化,因为在FunctorImpl中不是都有参数Param1和Param2,解决办法是设计一个基类,在基类中定义,然后子类中再去覆盖掉,具体实现如下:

#pragma  once

#include <memory>
#include <loki/Typelist.h>
// #include <loki/Sequence.h>
class NullType { };

class EmptyType { };

inline bool operator==(EmptyType const & lhs, EmptyType const & rhs)
{
return true;
}
inline bool operator<(EmptyType const & lhs, EmptyType const & rhs)
{
return false;
}
inline bool operator>(EmptyType const & lhs, EmptyType const & rhs)
{
return false;
}

#define ParamDefine(NUM) typedef EmptyType Param##NUM

template <typename R>
class FunctorImplBase
{
public:
typedef R ResultType;
typedef FunctorImplBase<R> FunctorImplBaseType;
//typedef EmptyType Param1;
ParamDefine(1);
ParamDefine(2);
// 	ParamDefine(3);
// 	ParamDefine(4);
// 	ParamDefine(5);
// 	ParamDefine(6);
// 	ParamDefine(7);
// 	ParamDefine(8);
virtual ~FunctorImplBase() {}
};

template <typename R, class TList>
class FunctorImpl;


上面这已经能够实现了,其他对于Functor的值语义的实现,通过复制构造,赋值构造来实现。
    接着在继续添加成员函数指针的功能,如下:
template <class ParentFunctor, typename PointerToObj, typename PointerToMemFn>
class MemFunHandler : public ParentFunctor::Impl
{
typedef typename ParentFunctor::Impl Base;
typedef typename Base::ResultType ResultType;
typedef typename Base::Param1 Param1;
typedef typename Base::Param2 Param2;
public:
//MemFunHandler(PointerToObj const & pObj, PointerToMemFn  & pMenFn)
MemFunHandler(PointerToObj const & pObj, PointerToMemFn pMenFn)
: pObj_(pObj)
, pMemFn_(pMenFn)
{ }
ResultType operator() () { return ((*pObj_).*pMemFn_)(); }
ResultType operator() (Param1 p1) { return ((*pObj_).*pMemFn_)(p1); }
ResultType operator() (Param1 p1,Param2 p2) { ((*pObj_).*pMemFn_)(p1,p2); }
private:
PointerToObj pObj_;
PointerToMemFn pMemFn_;
};

现在讨论几个上面的设计细节
1.转发函数的成本
 
当参数ResultType operator()(Param1 p1,Param2 p2) { ((*pObj_).*pMemFn_)(p1,p2); }
         对于上面这个式子,如果Param1和Param2的复制成本高的话,是会带来效率影响的,那怎么减少转发成本呢?可以通过ResultType
operator() (Param1 &p1,Param2 &p2)设置为引用,但此处会带来问题,什么问题呢?

如果此处设置Param1为string&,那么p1的型别将会变成string&&,这显然不合理,所以我们需要一个工具,在传递过程中使用正确的型别进行传递,这个可以参考第二章的TypeTraits。

下面进入下一个议题,绑定问题

         绑定有什么作用呢?绑定能够将一定的(context)保存下来,使得调用的时候能够不仅呈现操作意图,还能够体现当时操作的环境对操作的影响。

         绑定怎么处理?绑定需要根据传入的Functor类型来进行处理,传经来的Functor类型有个Traits,可以通过这个类进行处理,而需要根据传递进来的参数来推导Functor类型,则必须是一个函数模板。根据以上的假设,然后要需要注意的一个问题是在设计Functor的构造函数的时候,要补上复制构造函数和对aoto_ptr的构造函数,而怎么保证不同子类在复制的时候得够得到确定的类别,可以通过在基类中设置一个虚函数DoClone()来实现,子类通过实现这个函数,从而能够实现不同的子类的复制工作。关键代码如下:

 
virtual FunctorImplBase * DoClone() const = 0;
template <class U>
static U * Clone( U* pObj )
{
if ( !pObj ) return 0;
U * pClone = static_cast<U*>(pObj->DoClone());
assert( typeid(pClone) == typeid(pObj) );
return pClone;
}

补充:

对clone的补充,下面重新设计一个代码来进行测试:

#pragma once

#include <iostream>
#include <memory>
class Animal
{
public:
virtual void Speak() = 0;
virtual Animal * DoClone() const = 0;
virtual ~Animal() { }
};

#define DEFINE_CLONE_ANIMAL(Cls) \
virtual  Cls * DoClone() const { return new Cls(*this); }

class Bird : public Animal
{
public:
virtual void Speak() { std::cout << "I am a bird.\n"; }
DEFINE_CLONE_ANIMAL(Bird);
};

class Fish : public Animal
{
public:
virtual void Speak() { std::cout << "I am a fish.\n"; }
DEFINE_CLONE_ANIMAL(Fish);
};

class AnimalHolder
{
public:
AnimalHolder(AnimalHolder const &rhs) : spImpl_( rhs.spImpl_->DoClone() ) {}
AnimalHolder(Animal * spImpl ) : spImpl_( std::auto_ptr<Animal>(spImpl) ) { }
void Speak() { spImpl_->Speak(); }
private:
std::auto_ptr<Animal> spImpl_;
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c++ 设计模式