模式虽然精妙,却难完美,比如观察者模式中观察者生命周期的问题;比如访问者模式中循环依赖的问题等等;其它很多模式也存在这样那样的一些不足之处,如使用场景受限、实现复杂、不够简洁、不够通用等。但我觉得不足之处大都是可以采取一些手法去弥补去改进的,比如用c++11的新特性来改进。因此,便有了c++11改进我们的模式这个系列。这次我要讲的是如何使用c++11改进命令模式。

关于命令模式

  命令模式的作用是将请求封装为一个对象,将请求的发起者和执行者解耦,支持对请求排队以及撤销和重做。它的类图如下:


  由于将请求都封装成一个个命令对象了,使得我们可以集中处理或者延迟处理这些命令请求,而且不同的客户对象可以共享这些命令,还可以控制请求的优先级、排队、支持请求命令撤销和重做等等。命令模式的这些好处是显而易见的,但是,在实际使用过程中它的问题也暴露出来了。随着请求的增多,请求的封装类--命令类也会越来越多,尤其是GUI应用中,请求是非常多的。越来越多的命令类会导致类爆炸,难以管理。关于类爆炸这个问题,GOF很早就意识到了,他们提出了一个解决方法:对于简单的不能取消和不需要参数的命令,可以用一个命令类模板来参数化该命令的接收者,用接收者类型来参数化命令类,并维护一个接收者对象和一个动作之间的绑定,而这一动作是用指向同一个成员函数的指针存储的。具体代码是这样的:
简单命令类的定义:

构造器存储接收者和对应实例变量中行为。Execute操作实施接收者的这个动作。

为创建一个调用MyClass类的一个实例上的Action行为的Command对象,仅需要如下代码:

  通过一个泛型的简单命令类来避免不断创建新的命令类,是一个不错的办法,但是,这个办法不完美,即它只能是简单的命令类,不能对复杂的,甚至所有的命令类泛化,这是它的缺陷,所以,它只是部分的解决了问题。我想我可以改进这个办法缺陷,完美的解决类爆炸的问题。在c++11之前我不知道有没有人解决过这个问题,至少我没看到过。现在可以通过c++11来完美的解决这个问题了。

c++11改进命令模式

  要完美的解决命令模式类爆炸问题的关键是如何定义个通用的泛化的命令类,这个命令类可以泛化所有的命令,而不是GOF提到的简单命令。我们再回过头来看看GOF中那个简单的命令类的定义,它只是泛化了没有参数和返回值的命令类,命令类内部引用了一个接收者和接收者的函数指针,如果接收者的行为函数指针有参数就不能通用了,所以我们要解决的关键问题是如何让命令类能接受所有的成员函数指针或者函数对象。
  有没有一个能接受所有成员函数、普通函数和函数对象的类呢?有,在c++11中可以有,我上一篇博文中提到了一个万能的函数包装器,它可以接受所有的函数对象、fucntion和lamda表达式,它行不行呢?不行,因为它还不够完美,它还不能接受成员函数呢,所以它还不是真正的万能的函数包装器。我打算在它的基础上再扩展一下,让它为一个真正的万能的函数包装器。
  接受function、函数对象、lamda和普通函数的包装器:

template< class F, class... Args, class = typename std::enable_if<!std::is_member_function_pointer<F>::value>::type>
void Wrap(F && f, Args && ... args)
{
return f(std::forward<Args>(args)...);
}

  接受成员函数的包装器:

template<class R, class C, class... DArgs, class P, class... Args>
void Wrap(R(C::*f)(DArgs...), P && p, Args && ... args)
{
return (*p.*f)(std::forward<Args>(args)...);
}

  通过重载的Wrap让它能接收成员函数。这样一个真正意义上的万能的函数包装器就完成了。现在再来看,它是如何应用到命令模式中,完美的解决类爆炸的问题。

  一个通用的泛化的命令类:

#include <functional>
#include <type_traits>
template<typename R=void>
struct CommCommand
{
private:
std::function < R()> m_f; public:
template< class F, class... Args, class = typename std::enable_if<!std::is_member_function_pointer<F>::value>::type>
void Wrap(F && f, Args && ... args)
{
m_f = [&]{return f(args...); };
} template<class C, class... DArgs, class P, class... Args>
void Wrap(R(C::*f)(DArgs...) const, P && p, Args && ... args)
{
m_f = [&, f]{return (*p.*f)( args...); };
} // non-const member function
template<class C, class... DArgs, class P, class... Args>
void Wrap(R(C::*f)(DArgs...), P && p, Args && ... args)
{
m_f = [&, f]{return (*p.*f)( args...); };
} R Excecute()
{
return m_f();
}
};

测试代码:

struct STA
{
int m_a;
int operator()(){ return m_a; }
int operator()(int n){ return m_a + n; }
int triple0(){ return m_a * ; }
int triple(int a){ return m_a * + a; }
int triple1() const { return m_a * ; }
const int triple2(int a) const { return m_a * +a; } void triple3(){ cout << "" <<endl; }
}; int add_one(int n)
{
return n + ;
} void TestWrap()
{ CommCommand<int> cmd;
// free function
cmd.Wrap(add_one, ); // lambda function
cmd.Wrap([](int n){return n + ; }, ); // functor
cmd.Wrap(bloop);
cmd.Wrap(bloop, );

STA t = { 10 };
int x = ;
// member function
cmd.Wrap(&STA::triple0, &t);
cmd.Wrap(&STA::triple, &t, x);
cmd.Wrap(&STA::triple, &t, ); cmd.Wrap(&STA::triple2, &t, );
auto r = cmd.Excecute(); CommCommand<> cmd1;
cmd1.Wrap(&Bloop::triple3, &t);
cmd1.Excecute();
}

  我们在通用的命令类内部定义了一个万能的函数包装器,使得我们可以封装所有的命令,增加新的请求都不需要重新定义命令了,完美的解决了命令类爆炸的问题。

c++11 boost技术交流群:296561497,欢迎大家来交流技术。

(原创)c++11改进我们的模式之改进命令模式的更多相关文章

  1. 模式PK:命令模式VS策略模式

    1.概述 命令模式和策略模式的类图确实很相似,只是命令模式多了一个接收者(Receiver)角色.它们虽然同为行为类模式,但是两者的区别还是很明显的.策略模式的意图是封装算法,它认为“算法”已经是一个 ...

  2. 【设计模式】 模式PK:命令模式VS策略模式

    1.概述 命令模式和策略模式的类图确实很相似,只是命令模式多了一个接收者(Receiver)角色.它们虽然同为行为类模式,但是两者的区别还是很明显的.策略模式的意图是封装算法,它认为“算法”已经是一个 ...

  3. Linux centosVMware vim 编辑模式、vim命令模式、vim实践

    一.编辑模式.命令模式 在一般模式下输入:或/可进入命令模式.在该模式下可进行走索某个字符或字符串,也可保存.替换.退出.显示行号等. /word:在光标之后查找一个字符串word,按n向后继续搜索 ...

  4. 《JAVA与模式》之命令模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述命令(Command)模式的: 命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transaction)模式. ...

  5. NET设计模式 第二部分 行为型模式(16):命令模式(Command Pattern)

    命令模式(Command Pattern) ——.NET设计模式系列之十七 TerryLee,2006年7月 概述 在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”.但在某些场合,比 ...

  6. 设计模式《JAVA与模式》之命令模式

    在阎宏博士的<JAVA与模式>一书中开头是这样描述命令(Command)模式的: 命令模式属于对象的行为模式.命令模式又称为行动(Action)模式或交易(Transaction)模式. ...

  7. CentOS下如何从vi编辑器插入模式退出到命令模式

    刚打了下关于vi编辑器的命令,发现一直退出不了.后来自己敲着敲着它就退出了,写博客记录下. 比如现在w文件夹下面有一个ww文件 我进入这个文本,输入命令 vi ww,未回车,情况如下 按了回车,就进入 ...

  8. 进入编辑模式、vim命令模式、vim实践

    第4周第5次课(4月13日) 课程内容:5.5 进入编辑模式5.6 vim命令模式5.7 vim实践 5.5 进入编辑模式 所谓编辑模式就是进入到一个可以编辑文本文档的模式,常规的方式就是按小i进入编 ...

  9. 设计者模式之GOF23命令模式

    命令模式Command 将一个请求封装为一个对象,从而使我们可用不同的请求对客户参数化:对请求排队或者记录请求日志,以及支持可撤销的操作.也称之为:动作Action模式,事务transaction模式 ...

随机推荐

  1. android布局layout中的一些属性

      1.可以使某些资源文件或UI组件可重用 <include layout="@layout/other"/>  2.定义一个文本编辑框,使用绝对定位 android: ...

  2. Android开发环境——Eclipse ADT相关内容汇总

     Android开发环境将分为SDK相关内容.Eclipse ADT相关内容.模拟器AVD相关内容.调试器DDMS相关内容.日志LogCat相关内容.连接驱动ADB相关内容.内存泄露检测工具MAT相关 ...

  3. DPDK的安装与绑定网卡(转)

    from:http://www.cnblogs.com/mylinuxer/p/4274178.html DPDK的安装与绑定网卡 DPDK的安装有两种方法: 第一种是使用dpdk/tools/set ...

  4. 微信小程序-携带参数的二维码条形码生成

    demo文件目录 index.js文件 //index.js var wxbarcode = require('../../utils/index.js'); Page({ data: { code: ...

  5. Android-- ArrayAdapter用法举例(转载)

    近期很多Android开发者来函表示对ArrayAdapter和BaseAdapter的区别不是很清楚,这里Android123简单说下他们的关系和用处,ArrayAdapter是从BaseAdapt ...

  6. 【HTML】前端性能优化之CDN和WPO的比较

    CDN通过将资源存储在更接近用户的位置,缩短到服务器的往返行程,加快页面加载时间来解决性能问题.WPO解决方案,如Radware的FastView,则在前端进行性能提升处理,使页面更有效地呈现在浏览器 ...

  7. apktool 在mac下的使用 -反编译安卓apk文件

    1.下载apktool 点击这里下载 ,里面有两个文件,一个是.jar,一个是自己写的脚本.sh 注:最新的apktool.jar 文件可以点击这里下载 .sh脚本是自写脚本可不用更新最新,下载的ja ...

  8. .NET Core 2.0 Cookie中间件 权限验证

    :在ConfigureServices添加Cookie中间件,使用自定义Scheme services.AddAuthentication(options=> { options.Default ...

  9. Android MediaScannerJNI源代码具体解释

    1.简单介绍 MediaScannerJNI的在MediaScanner中的地位可參考 Android MediaScanner 总纲 MediaScanner JNI文件名称: android_me ...

  10. Java 8 flatMap example

    Java 8 flatMap example In Java 8, Stream can hold different data types, for examples: Stream<Stri ...