[C++实现 设计模式(9)] : 命令模式

16 篇文章 0 订阅

情景描述

       作为项目经理 , 我们接到一个新的项目----为某家旅游公司建立一套内部管理系统 . 该项目的成员分组采用了常规的分工方式 , 分为 需求组(RequirementGroup , RG) , 美工组(PageGroup , PG) , 代码组(CodeGroup , CG) . 刚开始 , 客户(也就是旅行社 , 甲方)很乐意和我们每个组讨论 , 比如和需求组讨论需求 , 和美工组讨论页面 , 和代码组讨论实现 . 我们可以用一下类图来表示 :
在这里插入图片描述
看抽象类中的每个方法 , 都是一个命令语气 . 给出命令然后由相关的人员去执行 .
代码如下:

//抽象组
class Group
{
public:
	//甲乙双方分开办公 , 如果你要和某个组讨论 , 你首先要找到这个组 
	virtual void Find() = 0 ;
	//被要求增加功能
	virtual void Add() = 0 ;
	//被要求删除功能
	virtual void Delete() = 0 ;
	//被要求修改功能
	virtual void Change() = 0 ;
	//被要求给出所有的变更计划
	virtual void Plan() = 0 ;
};

//需求组
class RequirementGroup : public Group
{
public:
	//客户要求需求组过去和他们谈
	void Find()
	{ cout<<"找到需求组..."<<endl ; }

	//客户要求增加一项需求
	void Add()
	{ cout<<"客户要求增加一项需求..."<<endl ; }
	
	//客户要求修改一项需求
	void Change()
	{ cout<<"客户要求修改一项需求..."<<endl ; }

	//客户要求删除一项需求
	void Delete()
	{ cout<<"客户要求删除一项需求..."<<endl ; }

	//客户要求给出变更计划
	void Plan()
	{ cout<<"客户要求需求给出变更计划..."<<endl ; }
};

//美工组
class PageGroup : public Group
{
public:
	//首先这个美工组应该能找到吧 , 不然你和谁谈?
	void Find()
	{ cout<<"找到美工组..."<<endl ; }

	//美工被要求增加一个页面
	void Add()
	{ cout<<"客户要求增加一个页面..."<<endl ; }
	
	//客户要求对现有界面做修改
	void Change()
	{ cout<<"客户要求修改一个页面..."<<endl ; }

	//甲方是老大 , 要求删除一些页面
	void Delete()
	{ cout<<"客户要求删除一个页面....."<<endl ; }

	//所有的增 ,删 ,改 ,都要给出计划
	void Plan()
	{ cout<<"客户要求页面变更计划..."<<endl ; }
};

//代码组
class CodeGroup : public Group
{
public:
	//客户要求代码组过去和他们谈
	void Find()
	{ cout<<"找到代码组..."<<endl ; }

	//客户要求增加一项功能
	void Add()
	{ cout<<"客户要求增加一项功能..."<<endl ; }
	
	//客户要求修改一项功能
	void Change()
	{ cout<<"客户要求修改一项功能..."<<endl ; }

	//客户要求删除一项功能
	void Delete()
	{ cout<<"客户要求删除一项功能..."<<endl ; }

	//客户要求给出变更计划
	void Plan()
	{ cout<<"客户要求代码变更计划..."<<endl ; }
};

       整个项目的3个支柱都已经产生 , 那看客户怎么和我们谈 . 客户刚开始提交了他们自己写的一份比较完整的需求 , 需求组根据这份需求写了一份分析说明书 , 客户看了以后 , 要求增加需求.该场景的代码如下:

int main()
{
//首先客户找到需求组 , 过来谈需求 , 并修改
	cout<<"------------------客户要求增加一项需求---------------------"<<endl;
	Group* rg = new RequirementGroup();
	//找到需求组
	rg->Find();
	//增加一项需求
	rg->Add();
	//要求变更计划
	rg->Plan();

    return 0;
}

       客户的需求暂时满足了 , 过了一段时间 , 客户有要求"界面多画了一个 , 过来谈谈" , 于是又有一次场景变化 . 过了天后 , 客户又让代码组过去 , 说是数据库设计问题 , 然后又叫美工组过去 ,布置了一大堆命令…问题来了 , 每次都是叫一个组过去 , 布置个任务 , 然后出计划 , 每次都是这样 , 如果你是甲方 , 你烦不烦? 而且这种方式很容易出错误 .于是在原有的类图中增加一个Invoker类 (作为接头人), 其作用是根据客户的命令安排不同的组员进行工作 . 对客户发出的命令进行封装 , 每个命令是一个对象 , 避免客户 , 负责人 , 组员之间的交流误差 , 封装后的结果就是客户只要说一个命令 , 我的项目组就立刻开始启动 .
以下是完整的类图:
在这里插入图片描述

  • Command(抽象类) : 客户发给我们的命令 , 定义三个工作组的成员变量 , 供子类使用 ; 定义一个抽象方法execute, 由子类来实现 . (Command抽象类只有一个方法execute)
  • Invoker(实现类) : 项目街头负责人 , setCommand接收客户发给我们的命令 , action方法是执行客户的命令 . Command抽象类是整个扩展的核心.

完整代码如下 :

#include<iostream>
//#include<vld.h>

using namespace std;

//抽象组
class Group
{
public:
	//甲乙双方分开办公 , 如果你要和某个组讨论 , 你首先要找到这个组 
	virtual void Find() = 0 ;
	//被要求增加功能
	virtual void Add() = 0 ;
	//被要求删除功能
	virtual void Delete() = 0 ;
	//被要求修改功能
	virtual void Change() = 0 ;
	//被要求给出所有的变更计划
	virtual void Plan() = 0 ;
};

//需求组
class RequirementGroup : public Group
{
public:
	//客户要求需求组过去和他们谈
	void Find()
	{ cout<<"找到需求组..."<<endl ; }

	//客户要求增加一项需求
	void Add()
	{ cout<<"客户要求增加一项需求..."<<endl ; }
	
	//客户要求修改一项需求
	void Change()
	{ cout<<"客户要求修改一项需求..."<<endl ; }

	//客户要求删除一项需求
	void Delete()
	{ cout<<"客户要求删除一项需求..."<<endl ; }

	//客户要求给出变更计划
	void Plan()
	{ cout<<"客户要求需求给出变更计划..."<<endl ; }
};

//美工组
class PageGroup : public Group
{
public:
	//首先这个美工组应该能找到吧 , 不然你和谁谈?
	void Find()
	{ cout<<"找到美工组..."<<endl ; }

	//美工被要求增加一个页面
	void Add()
	{ cout<<"客户要求增加一个页面..."<<endl ; }
	
	//客户要求对现有界面做修改
	void Change()
	{ cout<<"客户要求修改一个页面..."<<endl ; }

	//甲方是老大 , 要求删除一些页面
	void Delete()
	{ cout<<"客户要求删除一个页面....."<<endl ; }

	//所有的增 ,删 ,改 ,都要给出计划
	void Plan()
	{ cout<<"客户要求页面变更计划..."<<endl ; }
};

//代码组
class CodeGroup : public Group
{
public:
	//客户要求代码组过去和他们谈
	void Find()
	{ cout<<"找到代码组..."<<endl ; }

	//客户要求增加一项功能
	void Add()
	{ cout<<"客户要求增加一项功能..."<<endl ; }
	
	//客户要求修改一项功能
	void Change()
	{ cout<<"客户要求修改一项功能..."<<endl ; }

	//客户要求删除一项功能
	void Delete()
	{ cout<<"客户要求删除一项功能..."<<endl ; }

	//客户要求给出变更计划
	void Plan()
	{ cout<<"客户要求代码变更计划..."<<endl ; }
};


//抽象命令类
class Command
{
protected:
	//需求组
	RequirementGroup* rg; 
	//美工组
	PageGroup* pg ;
	//代码组
	CodeGroup* cg;
public:
	Command()
	{
		rg = new RequirementGroup();
		pg = new PageGroup();
		cg = new CodeGroup();
	}
	~Command()
	{
		delete rg;
		delete pg;
		delete cg;
	}
	//只有一个方法 , 你要我做什么事情
	virtual void execute() = 0 ;
};

//增加需求的命令
class AddRequirementCommand : public Command
{
	//执行增加一项需求的命令
	void execute()
	{
		//找到需求组
		rg->Find();
		//增加一份需求
		rg->Add();
		//给出计划
		rg->Plan();
	}
};

//删除页面的命令
class DeletePageCommand : public Command
{
	//执行删除一个页面的命令
	void execute()
	{
		//找到页面组
		pg->Find();
		//删除一个页面 , 注 这里是需求组改
		rg->Delete();
		//给出计划
		rg->Plan();
	}
};

//负责人
class Invoker
{
public:
	//客户发出命令
	void setCommand(Command* com)
	{
		_command = com;
	}
	//执行客户的命令
	void action()
	{
		_command->execute();
	}
private:
	Command* _command;
};


int main()
{
	//定义我们接头人 , 小白
	Invoker* xiaoBai = new Invoker();
	cout<<"------------------客户要求增加一项需求---------------------"<<endl;
	//客户给我们下命令来
	Command* command = new AddRequirementCommand();
	//接头人收到命令
	xiaoBai->setCommand(command);
	//接头人执行命令
	xiaoBai->action();

	delete xiaoBai;
	xiaoBai = nullptr;
	delete command;
	command = nullptr;


	//如果要删除一个页面 , 仅需要修改的地方
	//Command* command = new DeletePageCommand();


	return 0;
}

运行结果 :
在这里插入图片描述

命令模式的定义

定义 : 将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能 .

命令模式的通用类图 :
在这里插入图片描述
命令模式的结构 :

  • Receive接收者角色
    该角色就是干活的角色 , 命令传递到这里是应该被执行的 , 具体到我们上面的例子中就是Group的三个实现类 .
  • Command命令角色
    需要执行的所有命令都在这里声明 .
  • Inovker调用者角色
    接收到命令 , 并执行命令 .在例子中 , 我(项目经理)就是这个角色 .
  • ConcreteCommand
    Command类的实现类,对抽象类中声明的方法进行实现 .
  • Client
    最终的客户端调用类 .

        命令模式比较简单 , 但是在项目中非常频繁的使用 , 因为它的封装性非常好 , 把请求方(Invoker)和执行方(Receiver)分开了 , 扩展性也有很好的保障 .

命令模式的应用

命令模式的优点
  • 类间解耦
    调用者角色与接收者角色之间没有任何依赖关系,调用者实现功能时只需调用Command抽象类的execute方法就可以,不需要了解到底是哪个接收者执行 .
  • 可扩展性
    Command的子类可以非常容易地扩展,而调用者Invoker和高层次的模块Client不产生严重的代码耦合 .
  • 命令模式结合其他模式会更优秀
    命令模式可以结合责任链模式,实现命令族解析任务 ; 结合模板方法模式,则可以减少Command子类的膨胀问题 .
命令模式的缺点

       命令模式也是有缺点的,请看Command的子类:如果有N个命令,问题就出来了,Command的子类就可不是几个,而是N个,这个类膨胀得非常大,这个就需要读者在项目中慎重考虑使用 .

命令模式的使用场景

       只要你认为是命令的地方就可以采用命令模式,例如,在GUI开发中,一个按钮的点击是一个命令,可以采用命令模式 ; 模拟DOS命令的时候,当然也要采用命令模式 ; 触发-反馈机制的处理等 .

参考书籍 :

                  <<设计模式之禅 第二版>>
                  <<设计模式>>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值