14.Command命令(行为型模式)

Posted zzyzxb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了14.Command命令(行为型模式)相关的知识,希望对你有一定的参考价值。

一、动机(Motivation)

<1>在软件构建过程中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合——比如需要对行为进行“记录、撤销/重做(undo/redo)、事务”等处理,这种无法抵御变化的紧耦合是不合适的。
<2>在这种情况下,如何将“行为请求者”与“行为实现者”解耦?将一组行为抽象为对象,可以实现二者之间的松耦合。

二、意图(Intent)

将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
——《设计模式》GoF

三、结构(Structure)

四、结构详解

五、生活中的例子

服务员接受顾客的点单,把它记在账单上封装。这个点单被排队等待烹饪。

六、实现

namespace Test

    public class 厨师
    
        //Action
        public void 烤鸡翅(int 数量)
        
            Console.WriteLine("厨师烤鸡翅:0", 数量);
        
        //Action
        public void 烤羊肉串(int 数量)
        
            Console.WriteLine("厨师烤羊肉串:0", 数量);
        
    

    //Command
    public abstract class 命令
    
        protected 厨师 _cook; //receiver接受者
        public 命令(厨师 cook)
        
            this._cook = cook;
        
        //Execute执行
        public abstract void 烧烤();

        //State状态
        public bool 执行完毕 = false;

        private int _数量;
        public int 数量
        
            get
            
                return this._数量;
            
            set
            
                if (value < 0)
                
                    Console.WriteLine("数量应>0");
                
                else
                
                    this._数量 = value;
                
            
        
    

    //ConcreteCommand具体命令
    public class 烤鸡翅命令 : 命令
    
        public 烤鸡翅命令(厨师 cook) : base(cook)
        
        
        public override void 烧烤()
        
            base._cook.烤鸡翅(base.数量);
            this.执行完毕 = true;
        
    
    //ConcreteCommand具体命令
    public class 烤羊肉串命令 : 命令
    
        public 烤羊肉串命令(厨师 cook) : base(cook)
        
        
        public override void 烧烤()
        
            base._cook.烤羊肉串(base.数量);
            this.执行完毕 = true;
        
    

    //Invoker
    public class 服务员
    
        private int 鸡翅数量 = 60;
        private int 羊肉串数量 = 80;
        private System.Collections.Generic.List<命令> orders = new List<命令>();

        public void 下单(命令 command)
        
            if (command is 烤鸡翅命令)
            
                if (this.鸡翅数量 < command.数量)
                
                    Console.WriteLine("对不起,鸡翅不够了,现在只剩下:0", this.鸡翅数量);
                    return;
                
                else
                
                    this.鸡翅数量 -= command.数量;
                
            
            if (command is 烤羊肉串命令)
            
                if (this.羊肉串数量 < command.数量)
                
                    Console.WriteLine("对不起,羊肉串不够了,现在只剩下:0", this.鸡翅数量);
                    return;
                
                else
                
                    this.羊肉串数量 -= command.数量;
                
            
            orders.Add(command);
            Console.WriteLine("新下单:0,数量:1\\t2", command, command.数量, DateTime.Now);
        

        public void 取消订单(命令 command)
        
            if (command.执行完毕)
            
                Console.WriteLine("订单已执行完毕,东西都吃到肚子里了.不能再取消");
                return;
            
            orders.Remove(command);
            if (command is 烤鸡翅命令)
            
                this.鸡翅数量 += command.数量;
            
            else
            
                this.羊肉串数量 += command.数量;
            
            Console.WriteLine("订单已取消:" + command.ToString() + "\\t" + DateTime.Now.ToString());
        

        public void 全部执行()
        
            foreach (命令 cmd in orders)
            
                if (!cmd.执行完毕)
                
                    cmd.烧烤();
                
            
        
    

    internal class Program
    
        static void Main(string[] args)
        
            服务员 mm = new 服务员();
            厨师 cook = new 厨师();

            //30个鸡翅
            命令 cmd30个鸡翅 = new 烤鸡翅命令(cook);
            cmd30个鸡翅.数量 = 30;

            //40个羊肉串 
            命令 cmd40个羊肉串 = new 烤羊肉串命令(cook);
            cmd40个羊肉串.数量 = 40;

            Console.WriteLine("========先来30个鸡翅,40个羊肉串=========");

            mm.下单(cmd30个鸡翅);
            mm.下单(cmd40个羊肉串);

            mm.全部执行();

            Console.WriteLine("========实在太爽了,再来40个鸡翅,50个羊肉串=========");

            //40个鸡翅
            命令 cmd40个鸡翅 = new 烤鸡翅命令(cook);
            cmd40个鸡翅.数量 = 40;

            //50个羊肉串 
            命令 cmd50个羊肉串 = new 烤羊肉串命令(cook);
            cmd50个羊肉串.数量 = 50;

            mm.下单(cmd40个鸡翅);
            mm.下单(cmd50个羊肉串);
            mm.全部执行();

            Console.WriteLine("========不爽了,取消30个鸡翅,40个羊肉串=========");
            mm.取消订单(cmd30个鸡翅);
            mm.取消订单(cmd40个羊肉串);
            Console.ReadLine();
        
    

实现结果

七、适用性

<1>使用命令模式作为“CallBack”在面向对象系统中的替代。“CallBack”讲的便是先将一个函数登记上,然后在以后调用此函数;
<2>需要在不同的时间指定请求、将请求排队。一个命令对象和原先的请求发出者可以有不同的生命期。换言之,原先的请求发出者可能已经不在了,而命令对象本身仍然是活动的。这时命令的接收者可以是在本地,也可以在网络的另外一个地址。命令对象可以在串形化之后传送到另外一台机器上去;
<3>系统需要支持命令的撤消(undo)。命令对象可以把状态存储起来,等到客户端需要撤销命令所产生的效果时,可以调用undo()方法,把命令所产生的效果撤销掉。命令对象还可以提供redo()方法,以供客户端在需要时,再重新实施命令效果;
<4>如果一个系统要将系统中所有的数据更新到日志里,以便在系统崩溃时,可以根据日志里读回所有的数据更新命令,重新调用Execute()方法一条一条执行这些命令,从而恢复系统在崩溃前所做的数据更新。

八、总结

<1>Command模式是非常简单而又优雅的一种设计模式,它的根本目的在于将“行为请求者”与“行为实现者”解耦;
<2>Command模式的根本目的在于将“行为请求者”与“行为实现者”解耦,在面向对象语言中,常见的实现手段是“将行为抽象为对象”;
<3>实现Command接口的具体命令对象ConcreteCommand有时候根据需要可能会保存一些额外的状态信息;
<4>通过使用Compmosite模式,可以将多个命令封装为一个“复合命令”MacroCommand;
<5>Command模式与C#中的Delegate有些类似。但两者定义行为接口的规范有所区别:Command以面向对象中的“接口-实现”来定义行为接口规范,更严格,更符合抽象原则;Delegate以函数签名来定义行为接口规范,更灵活,但抽象能力比较弱;
<6>使用命令模式会导致某些系统有过多的具体命令类。某些系统可能需要几十个,几百个甚至几千个具体命令类,这会使命令模式在这样的系统里变得不实际。

17行为型模式之命令模式

概念

  Command模式也叫命令模式 ,是行为设计模式的一种。Command模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数。

  在面向对象的程序设计中,一个对象调用另一个对象,一般情况下的调用过程是:创建目标对象实例;设置调用参数;调用目标对象的方法。

  但在有些情况下有必要使用一个专门的类对这种调用过程加以封装,我们把这种专门的类称作command类。

  整个调用过程比较繁杂,或者存在多处这种调用。这时,使用Command类对该调用加以封装,便于功能的再利用。-

  调用前后需要对调用参数进行某些处理。调用前后需要进行某些额外处理,比如日志,缓存,记录历史操作等。

 

角色和职责

技术图片

Command

  Command命令的抽象类。

ConcreteCommand

         Command的具体实现类。

Receiver

         需要被调用的目标对象。

Invorker

         通过Invorker执行Command对象。

 

适用于:

         是将一个请求封装为一个对象,从而使你可用不同的请求对客户端进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。

 

 

案例

//根据输入不同的命令,出售不同的水果

#include <iostream>
using namespace std;
#include "list"

class Vendor
{
public:
	void sailbanana()
	{
		cout << "卖香蕉" << endl;
	}
	void sailapple()
	{
		cout << "卖苹果" << endl;
	}
};

class Command
{
public:
	virtual void sail() = 0;
};

class BananaCommand : public Command
{
public:
	BananaCommand(Vendor *v)
	{
		m_v = v;
	}
	Vendor *getV(Vendor *v)
	{
		return m_v;
	}

	void setV(Vendor *v)
	{
		m_v = v;
	}
	virtual void sail()
	{
		m_v->sailbanana();
	}
protected:
private:
	Vendor *m_v;
};

class AppleCommand : public Command
{
public:
	AppleCommand(Vendor *v)
	{
		m_v = v;
	}
	Vendor *getV(Vendor *v)
	{
		return m_v;
	}

	void setV(Vendor *v)
	{
		m_v = v;
	}
	virtual void sail()
	{
		m_v->sailapple();
	}
protected:
private:
	Vendor *m_v;
};

class Waiter
{
public:
	Command *getCommand()
	{
		return m_command;
	}
	void setCommand(Command *c)
	{
		m_command = c;
	}
	void sail()
	{
		m_command->sail();
	}
protected:
private:
	Command *m_command;
};

class AdvWaiter
{
public:
	AdvWaiter()
	{
		m_list = new list<Command *>;
		m_list->resize(0);
	}
	~AdvWaiter()
	{
		delete m_list;
	}
	void setCommands(Command *c)
	{
		m_list->push_back(c);
	}
	list<Command *> * getCommands()
	{
		return m_list;
	}
	void sail()
	{
		for (list<Command *>::iterator it=m_list->begin(); it!=m_list->end(); it++ )
		{
			(*it)->sail();
		}
	}
protected:
private:
	list<Command *> *m_list;
};

//小商贩 直接 卖 水果
void main25_01()
{
	Vendor *v = new Vendor;
	v->sailapple();
	v->sailbanana();

	delete v;
	return ;
}

//小商贩 通过命令 卖 水果
void main25_02()
{
	Vendor *v = new Vendor;
	AppleCommand *ac = new AppleCommand(v);
	ac->sail();

	BananaCommand *bc = new BananaCommand(v);
	bc->sail();

	delete bc;
	delete ac;
	delete v;
}

//小商贩 通过waiter 卖 水果
void main25_03()
{
	Vendor *v = new Vendor;
	AppleCommand *ac = new AppleCommand(v);
	BananaCommand *bc = new BananaCommand(v);

	Waiter *w = new Waiter;
	w->setCommand(ac);
	w->sail();

	w->setCommand(bc);
	w->sail();

	delete w;
	delete bc;
	delete ac;
	delete v;
}

//小商贩 通过advwaiter 批量下单 卖水果
void main25_04()
{
	Vendor *v = new Vendor;
	AppleCommand *ac = new AppleCommand(v);
	BananaCommand *bc = new BananaCommand(v);

	AdvWaiter *w = new AdvWaiter;
	w->setCommands(ac);
	w->setCommands(bc);
	w->sail();

	delete w;
	delete bc;
	delete ac;
	delete v;
}

void main()
{
	//main25_01();
	//main25_02();
	//main25_03();
	main25_04();
	system("pause");
}

  

 

以上是关于14.Command命令(行为型模式)的主要内容,如果未能解决你的问题,请参考以下文章

行为型模式:命令模式

手撸golang 行为型设计模式 命令模式

设计模式-行为型模式,命令模式(11)

设计模式-行为型-命令模式

6 行为型模式之 - 命令模式

行为型设计模式之命令模式