C++设计模式之命令模式

所属分类: 软件编程 / C 语言 阅读数: 48
收藏 0 赞 0 分享

前言

又要过年了,又是一个抢票季;从大学起,到现在工作,一直都是在外地,离家千里;以前买票,曾经也去火车站通宵排队买票;直到12306的腾空出现,在电脑前不停止的点着鼠标刷票,那个时候12306很是脆弱,抢一张票更是难上加难;现在好了,慢慢强大的12306,买票时出现了一个排队系统,先买票,进入12306的排队系统;然后,系统一个一个的处理大家的请求,一旦你的购票请求进入了排队系统,你就无法再次进行刷票了,除非你退出排队系统;这就减少了购票者的刷票次数;减少了12306后台服务器的处理压力。那么,你有没有想过,12306是如何将你的购票请求加入排队系统的呢?这样的排队系统是如何实现的呢?而我今天总结的命令模式,将会对此进行简单的剖析。

什么是命令模式?

在GOF的《设计模式:可复用面向对象软件的基础》一书中对命令模式是这样说的:将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。在OOP中,一切都是对象,将请求封装成对象,符合OOP的设计思想,当将客户的单个请求封装成对象以后,我们就可以对这个请求存储更多的信息,使请求拥有更多的能力;命令模式同样能够把请求发送者和接收者解耦,使得命令发送者不用去关心请求将以何种方式被处理。

我们在12306上,单击购票,这是一个请求,12306将这个请求封装为一个对象,在12306还没有上线排队系统时,你买票是这样的:你不停的用鼠标点击12306网站上的购票按钮,直到你买到了票;对于你的每一次点击,服务器都要进行处理,做出响应,告诉你,有没有买到票;这样,可能就会出现很多次无效的点击,但是这些无效的点击却增加了服务器的负担。增加了排队系统以后,你的购票请求就进入了对应的购票队列,一旦你进入了购票队列,当你再次鼠标单击购票时,12306会拒绝你的购票请求,它会告诉你,你已经进入了购票队列;处于购票队列中的你,你可以选择退出购票队列去购买其它车次的车票,从而进入其它购票队列。这样就有效的减少了购票者发送很多无效的购票请求。

这就好比票是共享资源,谁都想要,但是票的数量是一定的;在没有排队系统之前,大家的购票请求都是去竞争这个票,服务器对于大家对于共享资源——票的竞争进行互斥,谁抢到了,票就少一张;而现在有了购票队列以后,大家都不用去竞争了,按时间的先后顺序排好队,12306把票一张张的发给进入队列的购票者。

UML类图

Command:声明执行操作的接口;
ConcreteCommand:将一个接收者对象绑定于一个动作,之后,调用接收者相应的操作,以实现Execute来完成相应的命令;
Client:创建一个具体命令对象,但是并没有设定它的接收者;
Invoker:要求该命令执行这个请求;
Receiver:知道如何实施与执行一个请求相关的操作,任何类都可能作为一个接收者。

以上这些对象是按照下面的方式进行协作的:

1.Client创建一个ConcreteCommand命令对象,并指定它的Receiver对象;
2.Invoker对象存储该ConcreteCommand对象;
3.该Invoker通过调用Command对象的Execute操作来提交一个请求。如果这个命令请求是可以撤销的,ConcreteCommand就执行Execute操作之前存储当前状态以用于取消该命令请求;
4.ConcreteCommand对象调用Receiver的一些操作以执行该请求。

使用场合

使用命令模式实现12306(工程下载):
CHomePage类,表示12306的官网订票页面;
C12306Processor类,是后台真正处理用户的请求的类,专门进行出票;
Command类,表示用户的购票命令请求;
Customer类,表示购票的用户。
由于代码较多,这里只提供工程的下载。

这里再提供命令模式的一般实现:

复制代码 代码如下:

#include <iostream>
using namespace std;
 
#define SAFE_DELETE(p) if (p) { delete p; p = NULL; }
 
class Receiver
{
public:
     void Action()
     {
          cout<<"Receiver->Action"<<endl;
     }
};
 
class Command
{
public:
     virtual void Execute() = 0;
};
 
class ConcreteCommand : public Command
{
public:
     ConcreteCommand(Receiver *pReceiver) : m_pReceiver(pReceiver){}
     void Execute()
     {
          m_pReceiver->Action();
     }
private:
     Receiver *m_pReceiver;
};
 
class Invoker
{
public:
     Invoker(Command *pCommand) : m_pCommand(pCommand){}
     void Invoke()
     {
          m_pCommand->Execute();
     }
private:
     Command *m_pCommand;
};
 
int main()
{
     Receiver *pReceiver = new Receiver();
     Command *pCommand = new ConcreteCommand(pReceiver);
     Invoker *pInvoker = new Invoker(pCommand);
     pInvoker->Invoke();
     SAFE_DELETE(pInvoker);
     SAFE_DELETE(pCommand);
     SAFE_DELETE(pReceiver);
     return 0;
}

总结

命令模式是一个很经典的模式,我的理解也不会很到位;在我们的身边,就存在很多的使用命令模式的例子,数据库中的事务就是使用命令模式去实现的,在C#中的委托也是使用命令模式去实现的。我在这里只是将我在学习过程中理解到的东西记录了下来和大家分享。可能有的地方我的理解也存在差错,希望大家和我分享你对命令模式的理解。

更多精彩内容其他人还在看

C++广播通信实例

这篇文章主要介绍了C++实现广播通信的方法,实例讲述了C++ socket广播通信的原理与实现方法,需要的朋友可以参考下
收藏 0 赞 0 分享

C++计算ICMP头的校验和实例

这篇文章主要介绍了C++计算ICMP头的校验和的方法,代码简单实用,对于校验ICMP报文来说有不错的实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++设置超时时间的简单实现方法

这篇文章主要介绍了C++设置超时时间的简单实现方法,涉及系统函数setsockopt对套接口的操作,具有一定的实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++实现ping程序实例

这篇文章主要介绍了C++实现ping程序实例,涉及C++对于ICMP数据包的发送与回显处理,具有一定的实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++之boost::array的用法

这篇文章主要介绍了C++之boost::array的用法,以实例的形式简单讲述了静态数组的容器boost::array的使用技巧,具有一定的参考借鉴价值,需要的朋友可以参考下
收藏 0 赞 0 分享

C++之Boost::array用法简介

这篇文章主要介绍了C++之Boost::array用法简介,较为详细的分析了Boost::array中的常见用法,并用实例的形式予以总结归纳,需要的朋友可以参考下
收藏 0 赞 0 分享

VC文件目录常见操作实例汇总

这篇文章主要介绍了VC文件目录常见操作实例汇总,总结了VC针对文件目录的各种常用操作,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享

VC打印word,excel文本文件的方法

这篇文章主要介绍了VC打印word,excel文本文件的方法,是VC操作文本文件中非常实用的技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

VC++获得当前进程运行目录的方法

这篇文章主要介绍了VC++获得当前进程运行目录的方法,可通过系统函数实现该功能,是非常实用的技巧,需要的朋友可以参考下
收藏 0 赞 0 分享

VC中SendMessage和PostMessage的区别

这篇文章主要介绍了VC中SendMessage和PostMessage的区别,较为全面的分析了SendMessage和PostMessage运行原理及用法上的不同之处,非常具有实用价值,需要的朋友可以参考下
收藏 0 赞 0 分享
查看更多