模式虽然精妙,却难完美,比如观察者模式中观察者生命周期的问题;比如访问者模式中循环依赖的问题等等;其它很多模式也存在这样那样的一些不足之处,如使用场景受限、实现复杂、不够简洁、不够通用等。但我觉得不足之处大都是可以采取一些手法去弥补去改进的,比如用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 百度鹰眼轨迹SDK(v2.1.6)

    闲聊 看过<鹰眼追击>这部电影的读者一定对"鹰眼"这台巨无霸计算机印象深刻,如今我们能够实现自己的鹰眼. 效果图 本篇为百度地图SDK第三篇博文 第一篇实现:Andro ...

  2. 初次使用Microsoft Azure

    一.介绍 在微博上偶然发现微软的Azure有免费申请试用的机会,于是赶快给微软发邮件申请,第二天就通过了. 早就听说过微软在云计算方面发力,但一直没机会试用,之前用过国产的BAE.SAE,用GoAge ...

  3. 【JS】移动端 好用的分享插件 soshm.js

    参考链接:https://www.cnblogs.com/milo-wjh/p/6796082.html 对于qq内置浏览器分享功能处理:https://www.cnblogs.com/xuzheng ...

  4. how to Use the Tampermonkey API from the Chrome console

    1.Create the following script: // ==UserScript== // @name Exports some GM functions // @namespace Wh ...

  5. Ubuntu 13.04下安装WPS for Linux

    [日期:2013-06-03]   有人说Linux下不是有open office 和libre office么?是啊,可是将windows下的doc文档或者ppt放到Libreoffice上打开的时 ...

  6. 转 java实现截图

    转自 http://www.zhenhua.org/article.asp?id=382 可以直接运行! import java.awt.Dimension; import java.awt.Rect ...

  7. java 实现唯一ID生成器

      2014-11-08 内容存档在evernote,笔记名"java 实现唯一ID生成器"

  8. 使用itext直接替换PDF中的文本

    直接说问题,itext没有直接提供替换PDF中文本的接口(查看资料得到的结论是PDF不支持这种操作),不过存在解决思路:在需要替换的文本上覆盖新的文本.按照这个思路我们需要解决以下几个问题: itex ...

  9. python学习笔记——urllib库中的parse

    1 urllib.parse urllib 库中包含有如下内容 Package contents error parse request response robotparser 其中urllib.p ...

  10. ViewPager 源码分析(一) —— setAdapter() 与 populate()

    写在前面 做安卓也有一定时间了,虽然常用控件都已大致掌握,然而随着 Android N 的发布,不自觉的愈发焦虑起来.说来惭愧,Android L 的 Material Design 库里的许多控件都 ...