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

 更新时间:2014年10月08日 11:22:45   作者:果冻想  
这篇文章主要介绍了C++设计模式之命令模式,本文讲解了什么是命令模式、命令模式的使用场合等内容,并给出了一个代码实例,需要的朋友可以参考下

前言

又要过年了,又是一个抢票季;从大学起,到现在工作,一直都是在外地,离家千里;以前买票,曾经也去火车站通宵排队买票;直到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#中的委托也是使用命令模式去实现的。我在这里只是将我在学习过程中理解到的东西记录了下来和大家分享。可能有的地方我的理解也存在差错,希望大家和我分享你对命令模式的理解。

相关文章

  • VS2022调试通过海康摄像头烟火识别SDK的实现

    VS2022调试通过海康摄像头烟火识别SDK的实现

    本文主要介绍了VS2022调试通过海康摄像头烟火识别SDK的实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • 黑客帝国数字雨效果VC6源代码分享

    黑客帝国数字雨效果VC6源代码分享

    这篇文章主要介绍了黑客帝国数字雨效果VC6源代码分享,本文直接给出实现代码,Win7下编译通过,效果很酷,需要的朋友可以参考下
    2015-02-02
  • C++11/C++14中constexpr的使用案例详解

    C++11/C++14中constexpr的使用案例详解

    C++11规定,允许将变量声明为constexpr类型以便由编译器来验证变量的值是否是一个常量表达式,这篇文章主要介绍了C++11/C++14中constexpr的使用,需要的朋友可以参考下
    2023-06-06
  • 对for循环中表达式和循环体的执行顺序详解

    对for循环中表达式和循环体的执行顺序详解

    今天小编就为大家分享一篇对for循环中表达式和循环体的执行顺序详解,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-06-06
  • c++中vector的使用和模拟实现

    c++中vector的使用和模拟实现

    这篇文章主要介绍了c++中vector的使用和模拟实现,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-04-04
  • C语言实现统计一行字符串的单词个数

    C语言实现统计一行字符串的单词个数

    这篇文章主要介绍了C语言实现统计一行字符串的单词个数,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Effective C++ 学习笔记

    Effective C++ 学习笔记

    这篇文章主要介绍了Effective C++ 学习笔记,想学习Effective C++的朋友可以参考一下
    2023-08-08
  • C语言中设置用户识别码的相关函数的简单讲解

    C语言中设置用户识别码的相关函数的简单讲解

    这篇文章主要介绍了C语言中设置用户识别码的相关函数的简单讲解,包括setuid()函数和setreuid()函数以及setfsuid()函数,需要的朋友可以参考下
    2015-08-08
  • 浅谈c语言中类型隐性转换的坑

    浅谈c语言中类型隐性转换的坑

    下面小编就为大家带来一篇浅谈c语言中类型隐性转换的坑。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2016-08-08
  • C++ move()函数及priority_queue队列使用记录

    C++ move()函数及priority_queue队列使用记录

    move(obj)函数的功能是把obj当做右值处理,可以应用在对象的移动上,这篇文章主要介绍了C++ move()函数及priority_queue队列使用记录,需要的朋友可以参考下
    2023-01-01

最新评论