zoukankan      html  css  js  c++  java
  • 第16章 行为型模式—命令模式

    1. 命令模式(Command Pattern)的定义

    (1)定义

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

      ①封装请求:抽象出需要执行的动作,封装成对象(有统一的接口)。

      ②参数化:可以用不同的命令对象,去参数化配置客户的请求。(即,将命令对象作为参数去供Invoker调用)。

    (2)命令模式的结构和说明

     

      ①Command:定义命令的接口,声明执行的方法

      ②ConcreteCommand:命令接口实现对象,是“虚”的实现,通常会持有接收者调用接收者的功能来完成命令要执行的操作

      ③Receiver:接收者,真正执行命令的对象。任何类都可能成为一个接收者,只要它能够实现命令要求实现的相应功能。

      ④Invoker:要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。这个是客户端真正触发命令并要求命令执行相应操作的地方,也就是说相当于使用命令对象的入口。

      ⑤Client:创建具体的命令对象,并且设置命令对象的接收者。注意,这个不是我们常规意义上的客户端,而是组装命令对象和接收者。或许,把这个Client称为装配者会更好,因为真正使用命令的客户端是从Invoker来触发执行。

    【编程实验】打飞机游戏

     

    //行为型模式:命令模式
    //场景:打飞机游戏
    //角色控制器(如玩家)的主要操作:用导弹攻击、炸弹攻击来打飞机以及4个方向移动
    
    #include <iostream>
    #include <string>
    #include <vector>
    
    using namespace std;
    //***********************************************抽象命令接口***********************************
    //Command
    class FighterCommand
    {
    public:
        virtual void execute() = 0;
    };
    
    //*************************************************接收者**************************************
    //Receiver
    class Fighter
    {
    public:
        void missile()
        {
            cout << "用导弹攻击!" << endl;
        }
    
        void bomb()
        {
            cout << "用炸弹攻击!" << endl;
        }
    
        void move (int direction)
        {
            switch(direction)
            {
            case 1:
                cout << "向上移动!" << endl;
                break;
            case 2:
                cout << "向下移动!" <<endl;
                break;
            case 3:
                cout << "向左移动!" << endl;
                break;
            case 4:
                cout << "向右移动!" <<endl;
                break;
            default:
                cout << "不移动!" << endl;
            }
        }
    };
    
    //*************************************ConcreteCommand***************************************
    //导弹攻击命令
    class MissileCommand : public FighterCommand
    {
    private:
        Fighter* fighter;
    public:
        MissileCommand(Fighter* fighter)
        {
            this->fighter = fighter;
        }
    
        void execute()
        {
            fighter->missile();
        }
    };
    
    //炸弹攻击命令
    class BombCommand : public FighterCommand
    {
    private:
        Fighter* fighter;
    public:
        BombCommand(Fighter* fighter)
        {
            this->fighter = fighter;
        }
    
        void execute()
        {
            fighter->bomb();
        }
    };
    
    //移动命令
    class MoveCommand : public FighterCommand
    {
    private:
        Fighter* fighter;
        int direction;
    public:
        MoveCommand(Fighter* fighter, int direction)
        {
            this->fighter = fighter;
            this->direction = direction;
        }
    
        void execute()
        {
            fighter->move(direction);
        }
    };
    
    //***********************************************Invoker***********************************
    //请求者
    class Controller
    {
    private:
        FighterCommand* cmdMissible;
        FighterCommand* cmdBomb;
        FighterCommand* cmdMoveLeft;
        FighterCommand* cmdMoveRight;
    public:
        Controller(FighterCommand* missible, FighterCommand* bomb,
                   FighterCommand* left,     FighterCommand* right)
        {
            cmdMissible  = missible;
            cmdBomb      = bomb;
            cmdMoveLeft  = left;
            cmdMoveRight = right;
        }
    
        void missible()
        {
            cmdMissible->execute();
        }
    
        void bomb()
        {
            cmdBomb->execute();
        }
    
        void moveLeft()
        {
            cmdMoveLeft->execute();
        }
    
        void moveRight()
        {
            cmdMoveRight->execute();
        }
    };
    
    int main()
    {
        Fighter fighter; //战士(命令接收者)
    
        //命令对象
        FighterCommand* cmdMissible  = new MissileCommand(&fighter);
        FighterCommand* cmdBomb      = new BombCommand(&fighter);
        FighterCommand* cmdMoveLeft  = new MoveCommand(&fighter, 3);
        FighterCommand* cmdMoveRight = new MoveCommand(&fighter, 4);
    
        //玩家(命令发出者)
        //参数化:将命令对象作为参数传入Invoker
        Controller player(cmdMissible, cmdBomb, cmdMoveLeft, cmdMoveRight);
    
        player.bomb();
        player.missible();
        player.moveLeft();
        player.moveRight();
    
        //为什么不直接fighter.bomb()之类的呢?
        //有时当发出命令后,我们只关心任务是否完成?但任务具体由哪个Fighter执行
        //并不是我们关心的.如果直接调用,意味着命令的发出者直接叫某个具体的Fighter去做
        //这样两者的耦合太紧.利用命令模式可以将两者解耦。
    
        return 0;
    }

    (3)思考命令模式

      ①命令模式的本质封装请求。这是也命令模式的关键。把请求封装成对象,也就是命令对象,并定义了统一的执行操作的接口。这个命令对象可以被存储、转发、记录、处理、撤销等。整个命令模式都是围绕这个对象进行的。

      ②命令模式的动机:在软件构建过程中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。但在某些场合——比如需要对行为进行“记录、撤销/重做”、事务”等处理,这种无法抵御变化的紧耦合是不合适的,命令模式的动机就是将一组行为抽象为对象,可以实现二者之间的构耦合。

      ③命令模式的组装和调用

      命令的组装者用它维护命令的“虚”实现和真实实现之间的关系。(即下图中的Client,但将之定义成组装者更合适。真正的Client是通过Invoker来触发命令的)

      ④命令的接收者可以是任意的类,对它没有特殊要求。一个接收者可以处理多个命令,接收者提供的方法个数、名称、功能和命令对象中的可以不一样,只要能够通过调用接收者的方法来实现命令对应的功能就可以了。

      ⑤智能命令:在标准的命令模式中,命令的实现类是没有真正实现命令要求的功能的,真正执行命令的功能的是接收者。如果命令对象自己实现了命令要求的功能,而不再需要调用接收者,那这种情况称为智能命令。也有半智能的命令,即命令对象实现一部分,其他的还是需要调用接收者来完成,也就是说命令的功能由命令对象和接收者共同来完成。

    【编程实验】菜单项命令

     

    //行为型模式:命令模式
    //场景:菜单项命令
    /*
    要求:某软件公司欲开发一个基于Windows平台的公告板系统。系统提供一个主菜单(Menu),
    在主菜单中包含了一些菜单项(MenuItem),可以通过Menu类的addMenuItem()方法增加
    菜单项。菜单项的主要方法是click(),每一个菜单项包含一个抽象命令类,具体命令
    类包括OpenCommand(打开命令),CreateCommand(新建命令),EditCommand(编辑命令)
    等,命令类具有一个execute()方法,用于调用公告板系统界面类(Board)的open()、
    create()、edit()等方法。试使用命令模式设计该系统,使得MenuItem类与Board类
    的耦合度降低,绘制类图并编程模拟实现。
    */
    
    #include <iostream>
    #include <string>
    #include <map>
    
    using namespace std;
    //******************************Receiver********************
    //Board(公告板)
    class Board
    {
    public:
        void open()
        {
            cout <<"Board opened!" << endl;
        }
    
        void create()
        {
            cout << "Board create!" << endl;
        }
    
        void edit()
        {
            cout << "Board edit!" << endl;
        }
    };
    
    //****************************Command************************
    //Command命令接口
    class Command
    {
    public:
        virtual void execute() = 0;
    };
    
    //CreateCommand(“新建”)
    class CreateCommand : public Command
    {
    private:
        Board* board;
    public:
        CreateCommand(Board* board)
        {
            this->board = board;
        }
    
        void execute()
        {
            board->create();
        }
    };
    
    //OpenCommand(“打开”)
    class OpenCommand : public Command
    {
    private:
        Board* board;
    public:
        OpenCommand(Board* board)
        {
            this->board = board;
        }
    
        void execute()
        {
            board->open();
        }
    };
    
    //EditCommand(“编辑”)
    class EditCommand : public Command
    {
    private:
        Board* board;
    public:
        EditCommand(Board* board)
        {
            this->board = board;
        }
    
        void execute()
        {
            board->edit();
        }
    };
    
    //*****************************Invoker****************************
    //MenuItem(菜单项)
    class MenuItem
    {
    private:
        string itemName;
        Command* command;
    public:
        MenuItem(string name,Command* command)
        {
            this->command = command;
            this->itemName = name;
        }
    
        //单击事件
        void click()
        {
            //因菜单项(命令发送者)与具体的消息接收者(如board)的解耦
            //因此,对于不同的菜单项,这里都可以像如下那样处理。(简洁、灵活!)
            command->execute();
        }
    
        string& getItemName()
        {
            return itemName;
        }
    
        void setItemName(string name)
        {
            this->itemName = name;
        }
    
        Command* getCommand()
        {
            return command;
        }
    
        void setCommand(Command* newCommand)
        {
            this->command = newCommand;
        }
    };
    
    //Menu(菜单)
    class Menu
    {
    private:
        map<string,MenuItem*> items;
    public:
        void addMenuItem(MenuItem* item)
        {
            items[item->getItemName()] = item;
        }
    
        MenuItem* getMenuItemByName(string name)
        {
            return items[name];
        }
    };
    
    
    int main()
    {
        //创建公告板(命令接收者)
        Board board;
        //创建3个命令对象并与board组装起来
        CreateCommand createCmd(&board);
        OpenCommand openCmd(&board);
        EditCommand editCmd(&board);
    
    
        //创建主菜单及菜单项
        Menu menu;
        MenuItem* open = new MenuItem("open",&openCmd);
        MenuItem* create = new MenuItem("create", &createCmd);
        MenuItem* edit = new MenuItem("edit", &editCmd);
    
        menu.addMenuItem(open);
        menu.addMenuItem(create);
        menu.addMenuItem(edit);
    
        //测试
        //单击“创建公告板”菜单项
        menu.getMenuItemByName("create")->click();
        //单击“打开公告板”菜单项
        menu.getMenuItemByName("open")->click();
         //单击“编辑公告板”菜单项
        menu.getMenuItemByName("edit")->click();
    
        delete open;
        delete create;
        delete edit;
    
        return 0;
    }
    View Code

    2. 深度理解命令模式

    (1)参数化配置:用不同的命令对象,去参数化配置客户的请求。

    (2)可撤销的操作:

      ①补偿式(反操作式):如被撤销的操作是加的功能,那么反操作就是减的功能。

      ②存储恢复式:把操作前的状态记录下来,然后要撤销操作时就直接恢复回去就可以了。(该种方式会放到备忘录模式中进行讲解)

    【编程实验】可撤销/恢复操作的计算器

    //行为型模式:命令模式
    //场景:计算器(可撤销的计算)
    
    #include <iostream>
    #include <string>
    #include <list>
    
    using namespace std;
    
    //***************************************Receiver*******************
    //操作运算的接口
    class OperationApi
    {
    public:
        //获取计算完成后的结果
        virtual int getResult() = 0;
    
        //设计开始计算的初始值
        virtual void setResult(int result) = 0;
    
        //加法
        virtual void add(int num)=0;
    
        //减法
        virtual void substract(int num) = 0;
    };
    
    //运算类,真正实现加减法运算(具体的接收者)
    class Operation : public OperationApi
    {
    private:
        int result;
    public:
        int getResult()
        {
            return result;
        }
    
        void setResult(int result)
        {
            this->result = result;
        }
    
        void add(int num)
        {
            result +=num;
        }
    
        void substract(int num)
        {
            result -= num;
        }
    };
    
    //*************************Command***********************
    //命令接口,支持可撤销操作
    class Command
    {
    public:
        //执行命令的操作
        virtual void execute() = 0;
    
        //执行撤销的操作
        virtual void undo() = 0;
    };
    
    //具体的加法命令
    class AddCommand : public Command
    {
    private:
        //持有具体执行计算的对象(命令的接收者)
        OperationApi* operation;
    
        //要加上的数据
        int opeNum;
    public:
        AddCommand(OperationApi* operation, int opeNum)
        {
            this->opeNum = opeNum;
            this->operation = operation;
        }
    
        void execute()
        {
            operation->add(opeNum);
        }
    
        void undo()
        {
            operation->substract(opeNum);
        }
    };
    
    //具体的减法命令
    class SubstractCommand : public Command
    {
    private:
        //持有具体执行计算的对象(命令的接收者)
        OperationApi* operation;
    
        //要减去的数据
        int opeNum;
    public:
        SubstractCommand(OperationApi* operation, int opeNum)
        {
            this->opeNum = opeNum;
            this->operation = operation;
        }
    
        void execute()
        {
            operation->substract(opeNum);
        }
    
        void undo()
        {
            operation->add(opeNum);
        }
    };
    
    //*****************************Invoker************************************
    //计算器类,计算器上有加法按钮和减法按钮
    class Calculator
    {
    private:
        Command* addCmd; //加法命令对象
        Command* substractCmd; //减法命令对象
    
        //命令的操作历史记录,在撤销的时候用
        list<Command*> undoCmds;
    
        //命令被撤销的历史记录,在恢复时使用
        list<Command*> redoCmds;
    
    public:
        void setAddCmd(Command* addCmd)
        {
            this->addCmd = addCmd;
        }
    
        void setSubstractCmd(Command* subCmd)
        {
            this ->substractCmd = subCmd;
        }
    
        //提供给客户使用,执行加法功能
        void addPressed()
        {
            addCmd->execute();
    
            //把操作记录到历史记录里面
            undoCmds.push_back(addCmd);
        }
    
        //提供给客户使用,执行减法功能
        void substractPressed()
        {
            substractCmd->execute();
    
            //把操作记录到历史记录里面
            undoCmds.push_back(substractCmd);
        }
    
        void undoPressed()
        {
            if(undoCmds.size() > 0)
            {
                //取出最后一个命令来撤销
                Command* cmd = undoCmds.back();
                cmd->undo();
    
                //如果还有恢复功能,那就把这个命令记录到恢复历史列表中
                redoCmds.push_back(cmd);
    
                //然后把最后一个命令删除掉
                undoCmds.pop_back();
            }
            else
            {
                cout << "Sorry, nothing to undo()" << endl;
            }
        }
    
        void redoPressed()
        {
            if(redoCmds.size() > 0)
            {
                //取出最后一个命令来重做
                Command* cmd = redoCmds.back();
                cmd->execute();
    
                //把这个命令记录到可撤销的历史记录里面
                undoCmds.push_back(cmd);
    
                //然后把最后一个命令删除掉
                redoCmds.pop_back();
            }
            else
            {
                cout << "Sorry, nothing to redo()" << endl;
            }
        }
    };
    
    int main()
    {
        //客户端
        //1.组装命令和接收者
        //创建接收者
        OperationApi* operation = new Operation();
    
        //创建命令对象,并组装命令和接收者
        AddCommand addCmd(operation, 5);
        SubstractCommand subCmd(operation, 3);
    
        //2.把命令设置到持有者,也就是计算器里面
        Calculator calculator;
        calculator.setAddCmd(&addCmd);
        calculator.setSubstractCmd(&subCmd);
    
        //3. 模拟按下按钮,测试一下
        calculator.addPressed();
        cout << "一次加法运算后的结果为:" << operation->getResult() << endl;
    
        calculator.substractPressed();
        cout << "一次减法运算后的结果为:" << operation->getResult() << endl;
    
        //测试撤销
        calculator.undoPressed();
        cout << "撤销一次后的结果为:" << operation->getResult() << endl;
        calculator.undoPressed();
        cout << "再次撤销一次后的结果为:" << operation->getResult() << endl;
    
        //测试恢复
        calculator.redoPressed();
        cout << "恢复操作一次后的结果为:" << operation->getResult() << endl;
        calculator.redoPressed();
        cout << "再次恢复操作一次后的结果为:" << operation->getResult() << endl;
    
        return 0;
    }

    (3)宏命令

     

      ①宏命令(Macro Command)又称为组合命令,它是组合模式和命令模式联用的产物。宏命令是一个具体命令类,它拥有一个集合属性,在该集合中包含了对其他命令对象的引用。

      ②通常宏命令不直接与请求接收者交互,而是通过它的成员来调用接收者的方法。当调用宏命令的execute()方法时,将递归调用它所包含的每个成员命令的execute()方法,一个宏命令的成员可以是简单命令,还可以继续是宏命令。

      ③执行一个宏命令将触发多个具体命令的执行,从而实现对命令的批处理

    【编程实验】餐馆点菜

    //行为型模式:命令模式
    //场景:餐馆点菜
    //角色:
    //1.接收者:厨师,是命令的真正执行者。本例分为两种:做热菜的厨师和做凉菜的厨师
    //2.服务员:负责命令和接收者的组装,并持有命令对象(菜单)
    //          最后启动命令的也是服务员
    //3.命令对象:A、每一道菜是个命令对象;B、菜单(组合对象,由多道菜组成)
    
    #include <iostream>
    #include <string>
    #include <list>
    #include <typeinfo>
    
    using namespace std;
    
    //***************************************Receiver*******************
    //接收者:两种:做热菜的厨师和做凉菜的厨师
    //厨师的接口
    class CookApi
    {
    public:
        //做菜的方法
        //参数:菜名
        virtual void cook(string name) = 0;
    };
    
    //做热菜的厨师
    class HotCook : public CookApi
    {
    public:
        void cook(string name)
        {
            cout << "本厨师正在做: " << name <<"(热菜)" << endl;
        }
    };
    
    //做凉菜的厨师
    class CoolCook : public CookApi
    {
    public:
        void cook(string name)
        {
            cout << "凉菜" << name << "己经做好了,本厨师正在装盘。" << endl;
        }
    };
    
    //************************************Command**************************
    //命令接口,声明执行的操作
    class Command
    {
    public:
        virtual void execute() = 0;
    };
    
    //具体的命令对象(三道菜):
    //两道热菜:北京烤鸭、绿豆排骨煲
    //一道凉菜:蒜泥白肉
    
    //1.命令对象:北京烤鸭
    class DuckCommand: public Command
    {
    private:
        //持有具体做菜的厨师的对象
        CookApi* cookApi;
    public:
        void setCookApi(CookApi* cookApi)
        {
            this->cookApi = cookApi;
        }
    
        void execute()
        {
            cookApi->cook("北京烤鸭");
        }
    };
    
    //2. 命令对象:绿豆排骨煲
    class ChopCommand: public Command
    {
    private:
        //持有具体做菜的厨师的对象
        CookApi* cookApi;
    public:
        void setCookApi(CookApi* cookApi)
        {
            this->cookApi = cookApi;
        }
    
        void execute()
        {
            cookApi->cook("绿豆排骨煲");
        }
    };
    
    //3.命令对象:蒜泥白肉
    class PorkCommand: public Command
    {
    private:
        //持有具体做菜的厨师的对象
        CookApi* cookApi;
    public:
        void setCookApi(CookApi* cookApi)
        {
            this->cookApi = cookApi;
        }
    
        void execute()
        {
            cookApi->cook("蒜泥白肉");
        }
    };
    
    //4. 菜单对象,是个宏命令对象
    //A、本质上还是一个命令,要继承自Command
    //B、与普通命令不同,宏命令是多个命令的组合
    //C、执行宏命令相当于依次执行宏命令中包含的多个命令对象
    class MenuCommand : public Command
    {
    private:
        //用来记录组合本菜单的多道菜品,也就是多个命令对象
        list<Command*> lstCmds;
    public:
        //点菜,把菜品加入到菜单中
        //参数为客户点的菜
        void addCommand(Command* cmd)
        {
            lstCmds.push_back(cmd);
        }
    
        //执行菜单,其实就是循环执行菜单里面的每个菜
        void execute()
        {
            list<Command*>::iterator iter = lstCmds.begin();
            while( iter != lstCmds.end())
            {
                (*iter)->execute();
                ++iter;
            }
        }
    };
    
    //**************************************Invoker + Client*******************
    //服务员
    class Waiter
    {
    private:
        //持有一个宏命令对象(菜单)
        MenuCommand menuCmd;
    
        //
        HotCook hotCook;
        CoolCook coolCook;
    public:
        //客户点菜
        //参数客户点的菜,每道菜是一个命令对象
        void orderDish(Command* cmd)
        {
            //客户传过来的命令对象
            //在这里进行组装
    
    
            //判断到底是热菜还是凉菜
            if(typeid(*cmd) == typeid(DuckCommand))
            {
                 //交给热菜师傅
                ((DuckCommand*)cmd)->setCookApi(&hotCook);
            }
            else if(typeid(*cmd) == typeid(ChopCommand))
            {
                //交给热菜师傅
                ((ChopCommand*)cmd)->setCookApi(&hotCook);
            }
            else if(typeid(*cmd) == typeid(PorkCommand))
            {
                //交给凉菜师傅
                ((PorkCommand*)cmd)->setCookApi(&coolCook);
            }
    
            menuCmd.addCommand(cmd);
        }
    
        //客户点菜完毕,表示要执行命令了,这里就是执行菜单
        //这个组合命令了
        void orderOver()
        {
            menuCmd.execute();
        }
    };
    
    int main()
    {
        //客户端
    
        //客户只负责向服务员点菜就好了
        //创建服务员
        Waiter waiter;
    
        //创建命令对象,就是要点的菜
        Command* chop = new ChopCommand();
        Command* duck = new DuckCommand();
        Command* pork = new PorkCommand();
    
        //点菜,就是服务员记录下来
        waiter.orderDish(chop);
        waiter.orderDish(duck);
        waiter.orderDish(pork);
    
        //点菜完毕
        waiter.orderOver();
    
        delete chop;
        delete duck;
        delete pork;
    
        return 0;
    }
    /*输出结果:
    本厨师正在做: 绿豆排骨煲(热菜)
    本厨师正在做: 北京烤鸭(热菜)
    凉菜蒜泥白肉己经做好了,本厨师正在装盘。
    */
    View Code

    (4)队列请求

    //行为型模式:命令模式
    //场景:餐馆点菜(队列)
    //角色:
    //1.接收者:厨师,是命令的真正执行者。
    //2.服务员:负责命令和接收者的组装,并持有命令对象(菜单)
    //          最后启动命令的也是服务员
    //3.命令对象:A、每一道菜是个命令对象;B、菜单(组合对象,由多道菜组成)
    
    // * 编译命令:
    //*       g++ main.cpp -std=c++11
    
    #include <iostream>
    #include <string>
    #include <list>
    #include <thread> //C++ 11
    #include <mutex>
    
    using namespace std;
    
    //同步对象
    static mutex g_mutex;     //对队列进行同步
    static mutex g_PrintMutex;//对控制台输出同步
    
    class CookApi; //前向声明
    
    //************************************Command**************************
    //命令接口,声明执行的操作
    class Command
    {
    public:
        //执行命令对应的操作
        virtual void execute() = 0;
        //设置命令的接收者
        virtual void setCookApi(CookApi* cooker) = 0;
        //获取点菜的桌号
        virtual int getTableNum() = 0;
    
    };
    
    //命令队列类
    class CommandQueue
    {
    public:
        //用来存储命令对象的队列
        static list<Command*> cmds;
    public:
        //服务员传过来一个新的菜单,需要同步
        //同时,会有很多服务员传入菜单,而同时又有很多厨师从这个
        //队列中取值
        static void addMenu(list<Command*>& menu)
        {
            g_mutex.lock();
            list<Command*>::iterator iter = menu.begin();
            while (iter != menu.end())
            {
                cmds.push_back((*iter));
                ++iter;
            }
            g_mutex.unlock();
        }
    
        //厨师从命令队列里面获取命令对象进行处理(需要同步)
        static Command* getOneCommand()
        {
            Command* ret = NULL;
    
            g_mutex.lock();
    
            if (cmds.size() > 0)
            {
                //取出队列的第一个,因为是约定的按照加入的先后来处理
                ret = cmds.front();
                //同时从队列里面取掉这个命令对象
                cmds.pop_front();
            }
    
            g_mutex.unlock();
    
            return ret;
        }
    };
    
    list<Command*> CommandQueue::cmds;
    
    //***************************************Receiver*******************
    //接收者:两种:做热菜的厨师和做凉菜的厨师
    //厨师的接口
    class CookApi
    {
    public:
        //做菜的方法
        //参数:tableName-点菜的桌名,name-菜名
        virtual void cook(int tableNum, string name) = 0;
    };
    
    //做热菜的厨师
    class HotCook : public CookApi
    {
    private:
        string name; //厨师的名字
    public:
        HotCook(string name)
        {
            this->name = name;
        }
    
        void cook(int tableNum, string name)
        {
            g_PrintMutex.lock();
            cout << this->name << "厨师正在为" <<tableNum
                 <<"号桌做:"<< name << endl;
    
           int cookTime = rand() % 5;
    
           this_thread::sleep_for(chrono::seconds(cookTime));
    
           cout << this->name << "厨师为"<< tableNum << "号桌做好了:"
                << name << ",共计耗时=" << cookTime << "" <<endl;
    
           g_PrintMutex.unlock();
        }
    
        void startWork()
        {
            while (true)
            {
                //不断从命令队列里面获取命令对象
                Command* cmd = CommandQueue::getOneCommand();
                if (cmd != NULL)
                {
                    //说明取到命令对象了,这个命令对象还没有设置接收者
                    //因为前面都还不知道到底哪一个厨师来真正执行这个命令
                    //现在知道了,就是当前厨师实例,设置到命令对象里面
                    cmd->setCookApi(this);
                    //然后真正执行这个命令
                    cmd->execute();
                }
    
                //体息一下
                this_thread::sleep_for(chrono::milliseconds(1000));
            }
        }
    };
    
    //****************************************具体命令对象*******************************************************
    //具体的命令对象(三道菜):北京烤鸭、绿豆排骨煲、蒜泥白肉
    
    //1.命令对象:北京烤鸭
    class DuckCommand: public Command
    {
    private:
        //持有具体做菜的厨师的对象
        CookApi* cookApi;
    
        //点菜的桌号
        int tableNum;
    public:
        DuckCommand(int tableNum)
        {
            this -> tableNum = tableNum;
        }
    
        void setCookApi(CookApi* cookApi)
        {
            this->cookApi = cookApi;
        }
    
        int getTableNum(){return tableNum;}
    
        void execute()
        {
            cookApi->cook(tableNum, "北京烤鸭");
        }
    };
    
    //2. 命令对象:绿豆排骨煲
    class ChopCommand: public Command
    {
    private:
        //持有具体做菜的厨师的对象
        CookApi* cookApi;
    
        //点菜的桌号
        int tableNum;
    public:
        ChopCommand(int tableNum)
        {
            this -> tableNum = tableNum;
        }
    
        void setCookApi(CookApi* cookApi)
        {
            this->cookApi = cookApi;
        }
    
        int getTableNum(){return tableNum;}
    
        void execute()
        {
            cookApi->cook(tableNum, "绿豆排骨煲");
        }
    };
    
    //3.命令对象:蒜泥白肉
    class PorkCommand: public Command
    {
    private:
        //持有具体做菜的厨师的对象
        CookApi* cookApi;
    
        //点菜的桌号
        int tableNum;
    public:
        PorkCommand(int tableNum)
        {
            this -> tableNum = tableNum;
        }
    
        void setCookApi(CookApi* cookApi)
        {
            this->cookApi = cookApi;
        }
    
        int getTableNum(){return tableNum;}
    
        void execute()
        {
            cookApi->cook(tableNum, "蒜泥白肉");
        }
    };
    
    //4. 菜单对象,是个宏命令对象
    //A、本质上还是一个命令,要继承自Command
    //B、与普通命令不同,宏命令是多个命令的组合
    //C、执行宏命令相当于依次执行宏命令中包含的多个命令对象
    class MenuCommand : public Command
    {
    private:
        //用来记录组合本菜单的多道菜品,也就是多个命令对象
        list<Command*> lstCmds;
    public:
        //点菜,把菜品加入到菜单中
        //参数为客户点的菜
        void addCommand(Command* cmd)
        {
            lstCmds.push_back(cmd);
        }
    
        void setCookApi(CookApi* cooker)
        {
            //什么都不做,这个方法对组合命令对象的菜单没有意义
        }
    
        int getTableNum()
        {
            //什么都不做,这个方法对组合命令对象的菜单没有意义
            return 0;
        }
        //执行菜单
        void execute()
        {
            //这里与之前的例子不同,把菜单传给队列
            CommandQueue::addMenu(lstCmds);
        }
    
        list<Command*>& getCommands()
        {
            return lstCmds;
        }
    };
    
    //**************************************Invoker + Client*******************
    //服务员
    class Waiter
    {
    private:
        //持有一个宏命令对象(菜单)
        MenuCommand menuCmd;
    public:
        //客户点菜
        //参数客户点的菜,每道菜是一个命令对象
        void orderDish(Command* cmd)
        {
            menuCmd.addCommand(cmd);
        }
    
        //客户点菜完毕,表示要执行命令了,这里就是执行菜单
        //这个组合命令了
        void orderOver()
        {
            menuCmd.execute();
        }
    };
    
    //线程函数
    void thread_entry(HotCook* cooker)
    {
        cooker->startWork();
    }
    
    //客户端
    int main()
    {
        srand( time(NULL) );
    
        //创建三位厨师
        HotCook cook1("张三");
        HotCook cook2("李四");
        HotCook cook3("王五");
    
        //启动3个工作线程
        thread t1(thread_entry,&cook1);
        thread t2(thread_entry,&cook2);
        thread t3(thread_entry,&cook3);
    
        //为了简单,直接用循环模拟多个桌号点菜
        //创建服务员
        for(int i = 0; i < 5; i++)
        {
            Waiter* waiter = new Waiter();
    
            //创建命令对象,就是要点的菜
            Command* chop = new ChopCommand(i);
            Command* duck = new DuckCommand(i);
            Command* pork = new PorkCommand(i);
    
            //点菜,就是服务员记录下来
            waiter->orderDish(chop);
            waiter->orderDish(duck);
            waiter->orderDish(pork);
    
            //点菜完毕
            waiter->orderOver();
        }
    
        //为简化,这里省略了对Waiter、Commands对象的释放。。。
    
        t1.join();
        t2.join();
        t3.join();
    
        return 0;
    }

    (5)日志请求

      ①概念:把请求的历史记录保存下来,一般采用永久存储方式。如果在运行请求中,系统崩溃了,那么当再次运行时,就可以从保存的历史记录中获取日志请求,并重新执行命令。

      ②实现方法:在命令对象添加上存储和装载的方法,其实就是让命令对象自己实现类似序列化的功能。

    3. 命令模式的优缺点

    (1)优点:

      ①更松散的耦合:命令的发起者和具体实现命令的对象(接收者完全解耦)。也就是说发送者完全不知道具体实现对象是谁。

      ②更动态的控制:把请求封装起来,可以动态地对它进行参数化、队列化和日志化。

      ③很自然的复合命令:命令模式能够很容易地组合成复合命令,也就是宏命令,从而使系统操作更简单,功能更强大。

      ④更好的扩展性:扩展命令很容易,只需要实现新的命令对象,然后装配就可以使用,己有的实现完全不用变化。

    (2)缺点: 如果有N个命令,则Command的子类就需要N个,这会造成子类的膨胀

    4. 应用场景

    (1)需要抽象出执行的动作,并参数化这些对象时可选用命令模式

    (2)需要不同的时刻指定、排列和执行请求,可将请求封装成为命令对象,然后实现将请求队列化。

    (3)需要支持取消操作,可通过管理命令对象,能很容易实现命令的恢复和重做功能。

    (4)在需要事务的系统中,可选用命令模式,它提供了对事务进行建模的方法。

    5. 相关模式

    (1)命令模式和组合模式

    命令模式中,宏命令可使用组合模式.(但注意,之前的例子没有使用组合模式)

    (2)命令模式与备忘录模式

      ①命令模式中,实现可撤销功能时,有两种实现方法,其中一种是保存命令执行前的状态,撤销时把状态恢复。也可以考虑使用备忘录模式

      ②如果状态存储在命令对象中,也可以使用原型模式,把命令对象当作原型来克隆一个新的对象,然后将克隆出来的对象通过备忘录模式存放。

  • 相关阅读:
    C#中调用Outlook API 发起会议
    Log4Net配置
    web端调用Webapi获取Excel表格
    表格导出之Aspose.Cells
    验证输入框
    把新建的对象所有属性变成默认值
    省市区三级联动
    全局异常处理
    HttpHttpServletRequest / Reponse
    热部署
  • 原文地址:https://www.cnblogs.com/5iedu/p/5578970.html
Copyright © 2011-2022 走看看