命令模式_命令模式应用场景 - CSDN
精华内容
参与话题
  • 设计模式之命令模式

    千次阅读 2018-01-12 22:18:14
    说实话这个模式挺令人纠结的,但从...命令模式的定义:将请求封装成一个对象,从而让用户使用不同的请求把客户端参数化,以及支持可撤销和恢复的功能。 从定义上来看着实令人一脸懵逼,在这里描述一下博主个人的理解:

    说实话这个模式挺令人纠结的,但从这个模式的定义上来看,有点让人摸不到什么头脑,而且查看资料以后会发现还是有点稀里糊涂的,说懂了吧也很简单,也不懂吧也有不懂的理由,于是查阅手头的各种书籍,在此写下心得体会,算是加深一下印象。

    命令模式的定义:将请求封装成一个对象,从而让用户使用不同的请求把客户端参数化,以及支持可撤销和恢复的功能。

    从定义上来看着实令人一脸懵逼,在这里描述一下博主个人的理解:

    请求:客户端要求系统执行的操作,在java的世界里面就是某个对象的方法。

    Command:请求封装成的对象,该对象是命令模式的主角。也就是说将请求方法封装成一个命令对象,通过操作命令对象来操作请求方法。在命令模式是有若干个请求的,需要将这些请求封装成一条条命令对象,客户端只需要调用不同的命令就可以达到将请求参数化的目的。将一条条请求封装成一条条命定对象之后,客户端发起的就是一个个命令对象了,而不是原来的请求方法!

    Receiver:有命令,当然有命令的接收者对象:如果有只有命令,没有接受者,那不就是光棍司令了?没有电视机或者电脑主机,你对着电视机遥控器或者电脑键盘狂按有毛用?Receiver对象的主要作用就是受到命令后执行对应的操作。对于点击遥控器发起的命令来说,电视机就是这个Receiver对象,比如按了待机键,电视机收到命令后就执行了待机操作,进入待机状态。

    Client:但是有一个问题摆在眼前,命令对象现在已经有了,但是谁来负责创建命令呢?这里就引出了客户端Client对象,再命令模式中命令是有客户端来创建的。打个比方来说,操作遥控器的那个人,就是扮演的客户端的角色。人按下遥控器的不同按键,来创建一条条命令。

    Invoker:现在创建命令的对象Client也已经露脸了,它负责创建一条条命令,那么谁来使用或者调度这个命令呢?--命令的使用者就是Invoker对象了,还是拿人,遥控器,电视机来做比喻,遥控器就是这个Invoker对象,遥控器负责使用客户端创建的命令对象。该Invoker对象负责要求命令对象执行请求,通常会持有命令对象,可以持有很多的命令对象。

    上面的讲解着实有些啰嗦,下面就用看电视的人(Watcher),电视机(Television),遥控器(TeleController)来模拟一下这个命令模式,其中Watcher是Client角色,Television是Receiver角色,TeleController是Invoker角色。

    首先设计一个简单的电视机的对象:

    //电视机对象:提供了播放不同频道的方法
    public class Television {
    
        public void playCctv1() {
            System.out.println("--CCTV1--");
        }
    
        public void playCctv2() {
            System.out.println("--CCTV2--");
        }
    
        public void playCctv3() {
            System.out.println("--CCTV3--");
        }
    
        public void playCctv4() {
            System.out.println("--CCTV4--");
        }
    
        public void playCctv5() {
            System.out.println("--CCTV5--");
        }
    
        public void playCctv6() {
            System.out.println("--CCTV6--");
        }
    
    }

    电视机的类创建好了,本文会以“非命令模式“和“命令模式“两种实现看电视的不同之处,加深对命令模式的理解。

    非命令模式实现
    如果不用命令模式的话,其实实现看电视的功能很简单,首先设计一个看电视的人的类:Watcher;既然要看电视,所以Watcher内部需要持有一个电视机的引用。如此简单的Watcher搞定:

    //观看电视的死宅类
    public class Watcher {
        //持有一个
        public Television tv;
    
        public Watcher(Television tv) {
            this.tv = tv;
        }
    
        public void playCctv1() {
            tv.playCctv1();
        }
    
        public void playCctv2() {
            tv.playCctv2();
        }
    
        public void playCctv3() {
            tv.playCctv3();
        }
    
        public void playCctv4() {
            tv.playCctv4();
        }
    
        public void playCctv5() {
            tv.playCctv5();
        }
    
        public void playCctv6() {
            tv.playCctv6();
        }
    }

    所以简单的调用就实现了:

    public static void main(String args[]) {
            Watcher watcher = new Watcher(new Television());
            watcher.playCctv1();
            watcher.playCctv2();
            watcher.playCctv3();
            watcher.playCctv4();
            watcher.playCctv5();
            watcher.playCctv6();
        }

    执行结果:

    --CCTV1--
    --CCTV2--
    --CCTV3--
    --CCTV4--
    --CCTV5--
    --CCTV6--

    可以看出Watcher类和Television完全的耦合在一起了,目前本文的电视机对象只能播放六个电视台,如果需要增添全国所有主流卫视的话,需要做如下改动:
    1、修改Television对象,增加若干个的playXXTV()的方法来播放不同的卫视。
    2、修改Watcher,也添加若干个对应的playXXTV()的方法,调用Television的playXXTV(),如下:

     public void playXXTV() {
            tv.playXXTV();
        }

    但是这明显违背了“对修改关闭,对扩展开放“的重要设计原则。
    别的不说,就拿本看电视来说,比如调用playXXTV()的顺序是随即的,也就是你胡乱切换了一通:比如你沿着cctv1、cctv2、cctv3、cctv4、xxtv、yytv..nntv的顺序来看电视,也就是发生了如下调用:

            watcher.playCctv1();
            watcher.playCctv2();
            watcher.playCctv3();
            watcher.playCctv4();
            watcher.playCctv5();
            watcher.playCctv6();
            watcher.playXXtv();
            watcher.playYYtv();
            watcher.playNNtv();

    ,当前你在看nntv,如果你想看上一个看过的台也就是yytv,这很简单,只要在playNNtv() 后面,调用watcher.playYYtv();即可,但是如果你想要一直回退到cctv1呢?那么你就话发生如下可怕的调用:

            watcher.playCctv1();
            watcher.playCctv2();
            watcher.playCctv3();
            watcher.playCctv4();
            watcher.playCctv5();
            watcher.playCctv6();
            watcher.playXXtv();
            watcher.playYYtv();
            watcher.playNNtv();
    
    
            watcher.playYYtv();
            watcher.playXXtv();
            watcher.playCctv6();
            watcher.playCctv5();
            watcher.playCctv4();
            watcher.playCctv3();
            watcher.playCctv2();
            watcher.playCctv1();

    为什么会这样呢?因为对于之前播放的哪个卫视并没有记录功能。是时候让命令模式来出来解决 问题了,通过命令模式的实现,对比下就能体会到命令模式的巧妙之处。

    命令模式的实现

    1、设计一个抽象的命令类:
    在本系统中,命令的接收者对象就是电视机Tevevision了:

    public abstract class Command {
        //命令接收者:电视机
        protected Television television;
    
        public Command(Television television) {
            this.television = television;
        }
    
        //命令执行
        abstract void execute();
    }
    

    将播放各个卫视的操作封装成一个一个命令,实现如下:

    //播放cctv1的命令
    public class CCTV1Command extends Command {
        @Override
        void execute() {
            television.playCctv1();
        }
    }
    
    //播放cctv2的命令
    public class CCTV6Command extends Command {
        @Override
        void execute() {
            television.playCctv2();
        }
    }
    。。。。。。。。
    
    //播放cctv6的命令
    public class CCTV1Command extends Command {
        @Override
        void execute() {
            television.playCctv6();
        }
    }

    抽象类Command的几个子类实现也很简单,就是将电视机TeleVision对象的playXxTV方法分布于不同的命令对象中,且因为不同的命令对象拥有共同的抽象类,我们很容易将这些名利功能放入一个数据结构(比如数组)中来存储执行过的命令。

    命令对象设计好了,那么就引入命令的调用着Invoker对象了,在此例子中电视遥控器TeleController就是扮演的这个角色:

    public class TeleController {
        //播放记录
        List<Command> historyCommand = new ArrayList<Command>();
    
        //切换卫视
        public void switchCommand(Command command) {
            historyCommand.add(command);
            command.execute();
        }
    
        //遥控器返回命令
        public void back() {
            if (historyCommand.isEmpty()) {
                return;
            }
            int size = historyCommand.size();
            int preIndex = size-2<=0?0:size-2;
    
            //获取上一个播放某卫视的命令
            Command preCommand = historyCommand.remove(preIndex);
    
            preCommand.execute();
        }
    
    }
    

    很简答,遥控器对象持有一个命令集合,用来记录已经执行过的命令。新的命令对像作为switchCommand参数来添加到集合中,注意在这里就体现出了让上文所术的请求参数化的目的。且遥控器类也提供了back方法用来模拟真实遥控器的返回功能:
    所以main方法的实现如下:

           //创建一个电视机
            Television tv = new Television();
            //创建一个遥控器
            TeleController teleController = new TeleController();
    
            teleController.switchCommand(new CCTV1Command(tv));
            teleController.switchCommand(new CCTV2Command(tv));
            teleController.switchCommand(new CCTV4Command(tv));
            teleController.switchCommand(new CCTV3Command(tv));
            teleController.switchCommand(new CCTV5Command(tv));
            teleController.switchCommand(new CCTV1Command(tv));
            teleController.switchCommand(new CCTV6Command(tv));
            System.out.println("------返回上一个卫视--------");
            //模拟遥控器返回键
            teleController.back();
            teleController.back();

    执行结果如下:

    --CCTV1--
    --CCTV2--
    --CCTV4--
    --CCTV3--
    --CCTV5--
    --CCTV1--
    --CCTV6--
    ----------返回上一个卫视-------------
    --CCTV1--
    --CCTV5--

    从上面的例子我们可以看出,命令模式的主要特点就是将请求封装成一个个命令,以命令为参数来进行切换,达到请求参数化的目的,且还能通过集合这个数据结构来存储已经执行的请求,进行回退操作。而且如果需要添加新的电视频道,只需要添加新的命令类即可

    而非命令模式中,看电视的人和电视耦合在一起;而新的命令模式则使用一个遥控器就将人和电视机解耦。总让你抱着电视机你也不乐意不是?

    结合上述例子,最后用一个图来简单的表示命令模式。博主喜欢“斗图”.
    这里写图片描述

    好了,我对命令模式的理解到这里就结束了,如果大家发现有什么错误的地方,希望能不吝指正。至于命令模式的缺点,如果理解了该模式,他的缺点不是显而易见的吗?博主偷个懒就不再赘述。

    展开全文
  • 本文是对面向对象设计模式--命令模式(Command)的解析,主要分为定义解析、通过餐厅点餐案例、遥控器案例讲解命令模式、多案例练习加深对命令模式的理解、最后总结知识要点。 第一篇:定义解析 命令模式是GoF...

    本文是对面向对象设计模式--命令模式(Command)的解析,主要分为定义解析、通过餐厅点餐案例、遥控器案例讲解命令模式、多案例练习加深对命令模式的理解、最后总结知识要点。

    第一篇:定义解析

    命令模式是GoF四人帮整理的《设计模式-可复用面向对象软件基础》一书中23种设计模式中归类为行为型模式中的设计模式,23种设计模式根据它们的用途分为三大类:5种创建型模式、7种结构型模式、11种行为型模式。

    引自GoF四人帮对命令模式(Command)的定义与类图:

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

    将一个请求封装为一个对象,从而使你可用不同的请求对象对客户进行参数化;这一句点出了命令模式的核心思想,客户发出一个请求就像发出一个命令或指指令一样,需要有接收者接收这个请求,然后进行处理。不同的请求,可能需要不同的接收者接收和处理,如智能家庭遥控器通过一个遥控器控制家里的各种电器,打开或关闭不同的电器,它的接收者是不同的,它必须由不同的电器去处理,为了实现将客户与这些具体接收者进行解耦,我们将请求封装成一个对象并增加一个调用者角色Invoker,将客户发出的不同请求封装成不同的命令对象,不同的命令对象包含着处理对应请求的各种接收者对象,将命令对象传递给调用者对象,由调用者对象去调用通知接收者处理请求,调用者就相当于遥控器,客户发出打开或关闭不同电器的请求,不同的请求被封装成不同的命令对象,遥控器调用不同的命令对象来通知不同的接收者即电器来处理这个打开或关闭的请求。这样也实现了用不同请求对客户进行参数化,同时也容易扩展,当有新的电器需要控制时,只需要为其封装一个对应的命令对象即可,客户端和调用者者不需要修改代码。

    对请求排队或记录请求日志,以及支持可取消的操作;将请求封装成一个命令对象之后,可以在命令对象中添加日志,这些对象可以被传递或者使用队列进行排除执行,命令对象中可以实现相应的撤销方法,便可直接通过这个命令对象进行撤销请求。

     

    第二篇:餐厅点餐案例

    需求:客户填写点餐订单、餐厅服务员拿到订单,将订单放到订单柜台,并通知厨师开始准备餐点、厨师开始准备餐点、最后做出订单上的餐点。

    需求分析:

    一张订单封装了准备餐点的请求,把订单想象成一个用来请求准备餐点的对象,订单对象可以被传递;从女招待传递到订单柜台,然后喊一声“订单来了”就可以。

    从面向对象角度分析点餐案例:

    实现:

    1、把订单封装成一个请求,订单请求中包含:餐点的内容、用于执行制作餐点的方法orderUp()和具体制作餐点的厨师的引用chef。

    2、订单请求对象可以被传递,从客户传到了服务员,服务员调用订单中的执行制作餐点的方法orderUp()。

    分析:

    1)一张订单封装了准备餐点的请求,把订单想象成一个用来请求准备餐点的对象,订单对象可以被传递;

    2)女招待的工作很简单,接下顾客的订单,然后将订单放到柜台,并调用orderUp()方法,通知厨师准备餐点。

    3)厨师是一个对象,他真正知道如何准备餐点。一旦女招待调用orderUp()方法,厨师就接手,实现需要创建餐点的所有方法。

    4)客户和厨师之间彻底解耦;女招待和厨师之间也彻底解耦;

    通过这种实现方式:把“发出请求的对象”和“接受与执行这些请求的对象”分隔开来。

    第三篇:遥控器案例

    需求:

    1)遥控器有七组开关按钮,还有一个撤销按钮。

    2)这些开关按钮用来控制家电开关,如电灯、风扇、热水器、音响设备等。

    3)我们希望能够创建一组遥控器的API,让每个开关按钮能够控制一个电器。

    4)注意:要求提供扩展,能够方便未来添加新的电器控制开关。

    需求分析:

    1)目前有一个有很多开和关按钮的遥控器和很多不同的电器类。

    2)分离关注点:遥控器应该知道如何解读按钮按下的动作,然后发出正确的请求;

    3)但是遥控器不需要知道具体电器的处理细节,如如何打开热水器。

    4)我们不必让遥控器知道太多具体电器类的细节。

    采用命令模式实现:

    1)命令模式可将“动作的请求者”从“动作的执行者”对象中解耦。请求者是用户、请求是按下的遥控器开关按钮的请求、执行者是具体的电器、遥控器是调用者。

    2)客户发起开或关的请求、遥控器接收请求并调用相应的电器、电器进行相关的开或关操作。

    3)把具体电器的开或关请求封装成请求对象,如打开电灯请求、关闭电灯请求等。每一个按扭就对应一个请求对象,按下按扭即调用请求的执行方法。

    4)撤销按钮对应一个撤销请求,按下撤销按钮要调用对应请求的撤销方法。

    当按下按钮的时候,就可以请命令对象做相关的工作。遥控器并不需要知道工作内容是什么,只要有个命令对象能和正确的对象沟通,把事情做好就可以了。所以,遥控器和电灯对象解耦了。

    使用这个模式,我们能够创建一个API,将这些命令对象加载到对应的按钮开关,让遥控器的代码尽量保持简单。而把家电的工作和进行该工作的对象一起封装在命令对象中。

    命令模式:

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

    1)Client客户:客户负责创建一个ConcreteCommand,并设置接收者Receiver。

    2)Receiver接收者:知道如何进行必要的工作,实现这个接口,任何类都可以当接收者。

    3)Command命令接口:为所有命令声明了一个接口。调用命令对象的execute()方法,就可以让接收者进行相关动作。这个接口也具备一个undo()撤销操作。

    4)ConcreteCommand具体命令对象:定义了动作和接收者之间的绑定关系。调用者只要调用execute()就可以发出请求,然后由ConcreteCommand调用接收者的一个或多个动作。

    5)Invoker调用者持有一个命令对象,并在某个时间点调用命令对象的execute()方法,将请求付诸实行。

    6)命令对象将动作和接收者包进对象中。这个对象只暴露一个execute()方法,当此方法被调用的时候,接收者就会进行这些动作。从外面看,其他对象不知道究竟哪个接收者进行了哪些动作,只知道调用execute()方法,请求的目的就能达到。

    1)命令对象将动作和接收者包进对象中。

    2)这个对象只暴露一个execute()方法,当此方法被调用的时候,接收者就会进行这些动作。

    3)从外面看,其他对象不知道究竟哪个接收者进行了哪些动作,只知道调用execute()方法,请求的目的就能达到。

     

    第四篇:案例实践

    练习案例源码:https://github.com/chentian114/100-dayaction/tree/master/designpattern-1

    OrderDemo点餐案例:

    客户点餐,填写订单,招待员拿到订单将其放到订单栏,并通知厨师制作餐点。

    RemoteControlDemo遥控器案例:

    一个遥控器有多个按钮,每对按钮控制一个电器的开和关,以及支持撤销操作。并希望能够提供扩展。

    AudioPlayerDemo盒式收音机案例:

    用户有一个盒式录音机,此录音机有播音(Play)、倒带(Rewind)和停止(Stop)功能。

    案例分析:录音机的键盘便是请求者(Invoker)角色;客户是客户端角色,而录音机便是接收者角色。Command类扮演抽象命令角色,而PlayCommand、StopCommand和RewindCommand便是具体命令类。用户不需要知道播音(play)、倒带(rewind)和停止(stop)功能是怎么具体执行的,这些命令执行的细节全都由键盘(Keypad)具体实施。用户只需要在键盘上按下相应的键便可以了。

    GroupRequirementDemo项目组需求案例:

    客户会提不同的需求到项目组如:增加一个需求、修改一项需求、删除一个页面,项目组要对这些需求进行处理。项目组分为:需求组、美工组、研发组。增加一个需求需要需求组、美工组、研发组参与。删除一个页面需要需求组与研发组删除参与。

    需求分析:

    将客户需求封装成命令,客户只需要关注其所提的需求即可,而不需要关注项目组具体处理该任务的项目组内部信息。项目组长接到用户需求,对客户需求进行参数化,如:增加一个需求就是一个新增需求的命令,删除一个页面的需求就是一个删除页面的命令。命令里持有处理该任务的组目组成员的引用,并有一个统一的execute()方法调用具体项目组成员处理该需求。

    实践:在实际Receiver一般采用封闭的方式,减少Client对Receiver的依赖。如案例中的各Group组。

     

    第五篇:总结

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

    Receive接收者角色,处理命令

    Command命令角色,需要执行的请求

    Invoker调用者角色,接收命令,并执行命令

    命令模式把请求的发送者和请求的执行分割开,委派给不同的对象。

    客户端:创建一个具体的命令。请求者:负责调用命令对象执行请求。接收者:负责具体实施和执行一个请求。

    命令模式的优点:

    1)使得新的命令很容易加入到系统中

    2)能容易的设计一个命令队列

    3)可以容易实现撤销

    4)可以容易地将命令记入日志

    命令模式优点:

    1)更松散的耦合,将发起命令的客户端与具体处理命令的接收者完全解耦,客户端完全不知道接收者是什么样子。

    2)更动态的控制,把请求封装起来,可以动态的对请求进行参数化、队列化和日志化等,使系统更灵活。

    3)复合命令,很容易的组合命令,即宏命令,使系统功能更强大。

    4)更好的扩展,很容易添加新的命令。

    优点:

    1)类间解耦,调用者角色与接收者角色之间没有任何依赖关系。

    2)可扩展性,Command子类可以非常容易扩展,调用者Invoker和高层次的Client不产一严重的代码耦合。

    缺点:

    1)类数量随命令数量增长而增长。可能造成类数量过多。

    所谓宏命令简单点说就是包含多个命令的命令,是一个命令的组合。

    展开全文
  • 设计模式之------命令模式(Command Pattern)

    千次阅读 热门讨论 2019-03-05 18:48:27
    ①、什么是命令模式? 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。 是一种数据驱动的设计模式,它属于行为型模式,请求以命令的形式...

    一、概念

    ①、什么是命令模式?

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

          是一种数据驱动的设计模式,它属于行为型模式,请求以命令的形式包裹在对象中,并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象,该对象执行命令。

    ②、主要解决什么问题?

          在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关系,但某些场合,比如需要对行为进行记录、撤销或重做、事务等处理时,这种无法抵御变化的紧耦合的设计就不太合适。

    ③、如何解决?

           通过调用者调用接受者执行命令,顺序:调用者→接受者→命令。

    ④、何时使用?

           在某些场合,比如要对行为进行“记录、撤销/重做、事务”等处理,这种无法抵御变化的紧耦合是不合适的,在这种情况下,如何将“行为请求者”与“行为实现者”解耦,将一组行为抽象为对象,可以实现两者之间的松耦合。

    ⑤、优缺点?

    优点: 1、降低了系统耦合度。 2、新的命令可以很容易添加到系统中去。

    缺点:使用命令模式可能会导致某些系统有过多的具体命令类。

    二、图

    ①、结构图

    ②、拓展图

    三、代码

    ①、代码类图

    ②、代码

    客户端

        class Program
        {
            static void Main(string[] args)
            {   //开店前准备:烤肉厨师,烤肉菜单,服务员
                Barbecuer boy = new Barbecuer();                                    //通过Barbecuer类实例化一个boy对象,即烧烤者
                Command bakeMuttonCommand1 = new BakeMuttonCommand(boy);            // 多态的体现:实例化烤羊肉串对象1
                Command bakeMuttonCommand2 = new BakeMuttonCommand(boy);            //通过command类,实例化一个烤羊肉串命令2对象,并将值赋予给对象
                Command bakeChickenWingCommand1 = new BakeChickenWingCommand(boy);  //通过command 类,实例化一个烤翅命令1对象
                Waiter girl = new Waiter();                                         //实例化一个服务员
    
                //开门营业
                girl.SetOrder(bakeMuttonCommand1);                                  //设置订单;服务员接收命令
                girl.SetOrder(bakeMuttonCommand2);                                  //记录所点菜品
                girl.SetOrder(bakeChickenWingCommand1);
    
                girl.Notify();                                                      //服务员一次性通知烤肉厨师执行
                Console.Read();                
    
            }
        }

    服务员

     public class Waiter
        {
            private IList<Command> orders = new List<Command>();                    //聚集的体现,建立一个集合用来存放客户的订单
    
            public void SetOrder(Command command)                                   //服务员设置订单,即增加删除订单
            {
                if(command.ToString()=="命令模式.BakeChickenWingCommand")           //如果命令为烤鸡翅命令
                {
                    Console.WriteLine("服务员:鸡翅没有了,请点别的烧烤。");        //那么,服务员告知没有鸡翅了
                }
                else                                                                //否则
                {
                    orders.Add(command);                                            //将命令添加到总订单中
                    Console.WriteLine("增加订单:"+command.ToString()+ "时间"+DateTime.Now.ToString()); //并记录具体的时间,内容
                }
            }
            public void CancelOrder(Command command)
            {
                orders.Remove(command);
                Console.WriteLine("取消订单:" + command.ToString() + "时间" + DateTime.Now.ToString());
            }
            public void Notify()                                                    //通知执行
            {
                foreach (Command cmd in orders)                                     //遍历整个订单,并通知接受者
                {
                    cmd.ExcuteCommand();  
                }
            }
        }

    烧烤者

     public class Barbecuer
        {
            public void BakeMutton()                                              //创建烤羊肉的方法
            {
                Console.WriteLine("烤羊肉串!");
            }
            public void BakeChickenWing()                                         //实现烤鸡翅的方法
            {
                Console.WriteLine("烤鸡翅!");
            }
        }

    抽象命令类

     public abstract class Command
        {
            protected Barbecuer receiver;                                          //引用烧烤者,因为命令是给烧烤者的
            public Command(Barbecuer receiver)
            {
                this.receiver = receiver;                                          //命令构造函数,所传参数为烧烤者
            }
            public abstract void ExcuteCommand();                                  //创建一个抽象执行命令方法
    
        }

    具体命令类(烤羊肉,烤鸡翅)

     class BakeMuttonCommand:Command                                            //烤羊肉类命令继承命令
        {
            public BakeMuttonCommand(Barbecuer receiver):base(receiver)            //子类构造函数与父类构造函数参数的引用
            {
    
            }
            public override void ExcuteCommand()                                  //重写父类中的执行命令方法
            {
                receiver.BakeMutton();                                            //接受者执行烤羊肉命令
            }
    
        }
        class BakeChickenWingCommand:Command                                      //烤鸡翅命令继承命令
        {
            public BakeChickenWingCommand(Barbecuer receiver):base(receiver)      //构造函数的继承
            {
    
            }
            public override void ExcuteCommand()                                  //重写父类执行命令方法
            {
                receiver.BakeChickenWing();                                       //接受者实现烤鸡翅行为
            }
        }

    四、拓展

    http://www.cnblogs.com/wolf-sun/p/3618911.html?utm_source=tuicool(更深入的理解)

    以上便是命令模式的简单梳理,下一站走起^_^

    展开全文
  • 命令模式

    千次阅读 2018-10-22 09:47:29
    一、命令模式定义 命令大家都不会陌生,那么在开始命令模式之前,可以想象一下生活中的命令模式的特点: 如老板命令你完成一个OA项目是一个命令,接着看看其特点: 1、在上面的命令中,命令的执行者肯定是聪明的...

    一、命令模式定义

    命令大家都不会陌生,那么在开始命令模式之前,可以想象一下生活中的命令模式的特点:

    如老板命令你完成一个OA项目是一个命令,接着看看其特点:

    1、在上面的命令中,命令的执行者肯定是聪明的你了。具体的执行方法,可能是通过vs实现,或者是通过eclipse实现,由此看来:命令要有个命令的执行者,还要有个命令的执行方法。

    2、命令的发出者很明显是老板,老板还有个发出方法,可能是通过电话给你说,也可能给你邮件给你说,也可能是通过开会给你说。所以命令的发出者要有一个命令,还要有个发出的方法。

    3、最后看看命令,命令有个名字,命令的肯定要执行。而且命令是在boss给你发出通知后执行的。

    接下来看看命令模式的定义:

    命令模式:将请求封装成对象,以便使用不同的请求、日志、队列等来参数化其他对象。命令模式也支持撤销操作。

    每次讲一个模式时,从定义都不能体会其中的技巧,所以接着我会通过举例子来说明命令模式。

    二、命令模式的举例

    下面来看看多用遥控器是如何使用命令模式的。

    2.1需求

    假设某个公司需要设计一个多用功能的遥控器。基本的需求如下:

    该遥控器有可以控制风扇,白炽灯,热水器等等的多对开关,而且可能还有其他的电器,暂时不做其功能,但是希望可以保留接口,用的时间可以方便的扩展。

    除上面的需求之外,还需要有个按钮,可以撤销上一步的操作。基本功能如下图:

    image

    2.2问题

    在设计遥控器时,风扇,白炽灯,热水器的开关方法已经定义好,其名字各不相同。不妨设置其方法为如下:

    image

    由于各种电器的开关方法都不一样,而且还存在一个待扩展的电器,如果没有学习命名模式之前,我们在设置扩展的开关时,会出现的问题是什么呢?假设现在有电视,冰箱还可能会用到遥控器,那么我们会在最后一个开关上写if else,当然如果哪一天有多了一个大门也加入了我们的遥控的行列,这样我们继续加if else ,很显然随着电器的高速发展,会有多个需要遥控可以控制的。

    举个例子,如果我们是需要遥控的客户,现在有一款遥控如果有遥控可以进行扩展,一种是可以扩展指定类型的,像上面的,只能再去扩展电视和冰箱中的一种,偶尔有一天你看到隔壁邻居的门,也可以使用遥控了,所以你去把你的高级遥控器,拿到扩展店时,扩展工程师说了,现在只能扩展电视和冰箱,不支持对大门的遥控扩展.

    我们肯定是希望,可以自由的扩展,大门可以使用遥控了,就对大门扩展,车门使用遥控了,就对车门扩展……其实也就是一种松耦合的实现。

    2.3分析问题

    为了实现松耦合,我们现在来想一下,周末去请朋友吃饭,服务员mm问你吃什么,你说水煮活鱼,然后在菜单上面,写上水煮活鱼。下个星期天想吃花生米啤酒,同样也写在订单上,然后服务员mm把订单拿给厨师。

    在上面的例子中,无论你点了什么菜,服务员mm,只需要知道顾客点的什么菜,从而给不同的厨师做(虽然不是直接的,但最终凉菜会给凉菜的师傅做,热菜的会给热菜的厨师做),然而具体的怎么做是厨师的事情,服务员知道顾客点上面菜,只是为了能正确的交个厨师去做。

    其实在这个例子中,也是一个命令模式的例子,不同的订单对应的有不同的厨师,最终订单拿到厨师面前,就是对厨师下了个命令,要做菜了。每订单或者说是每种菜都对应着自己的厨师,所以,客服需要有订单的的引用,订单为了知道调用哪个厨师,需要有厨师引用;两个引用都是使用的组合。从而可以调用多种自己需要对象的方法来实现松耦合。这里记住:客服mm知道顾客点菜的目的是为了让不同的厨师去做。再直接一些就是为了使用不同的命令。

    上面的吃饭问题和我们的遥控器问题差不多,都是包含下命令,命令的执行者,以及命令的具体内容。如果还是有些不清楚的话,就用简单的程序模拟一下上面的过程:

     View Code

    上面的例子中,厨师的做法有可能不同,就像我们遥控器的开关一样,电器的开关方法不一定相同。如果要执行,只需要把这个方法包在命令的激发方法中,然后去调用具体的方法就可以了。

    尽管上面的例子有些牵强,但是还是模拟了命令的过程,为我们遥控器的设计提供了思路。

    2.4解决问题

    回到我们的遥控器问题上面来,我们可以先定义好我们的风扇,白炽灯,热水器。然后定义其分别的开关命令,每个命令都有自己对应的电器引用,而且会在命令的Excute中包装电器的开或者关,最后需要把命令安装到遥控器上面,在遥控器上每个按钮都对应有自己的激发方法,其代码如下:

     View Code

    这样基本上就实现了我们的现有的三种电器的遥控。需要注意的是,在开始初始化遥控器时,对每个命令初始化成了NoCommand,也就是什么都不执行。在命令的初始化经常使用,同时这也解决了我们的在扩展前什么都不做的难题。看了风扇,白炽灯,热水器的遥控实现,进一步的扩展任何的电器,相信都不是什么难事。但是还有个功能没有实现,就是撤销到上一步的操作,接下来我们就来实现撤销操作。

    2.5问题的补充说明

    撤销操作就想我们遥控中的返回一样。基本上就是灯亮着,突然按了一下关灯,然后再按一下返回键,灯就亮了。其他的电器同样的道理。下面先看一下灯的撤销原理,命令除了执行外还有一个撤销,所以我们需要先都命令的接口添加一个方法。

    /// <summary> 
    /// 命令接口 
    /// </summary> 
    public interface ICommand 

        void Excute(); 
        void Undo(); 
    }

    对于开灯需要做的修改如下:

    public class LightOnCommand : ICommand 
       { 
           Light light; 
           public LightOnCommand(Light light) 
           { 
               this.light = light; 
           } 
           public void Excute() 
           { 
               light.LightOn(); 
           } 
           /// <summary> 
           /// 调用命令的反命令 
           /// </summary> 
           public void Undo() 
           { 
               light.LightOff(); 
           } 
       }

    其他命令同理,代码会在源码中一并给出。也就是每个命令都有自己的反命令,在Undo方法里面也就是调用反命令的Excute方法。每当按下一个按钮时,就去记录其命令的名称,如果按撤销的话,就执行命名的Undo方法。下面给出主要代码:

    public void OnButtonWasPressed(int slot) 
       { 
           onCommands[slot].Excute(); 
           backCommand=onCommands[slot]; 
       } 
       public void OffButtonWasPressed(int slot) 
       { 
           offCommands[slot].Excute(); 
           backCommand = offCommands[slot]; 
       } 
       public void BackButtonWasPressed() 
       { 
           backCommand.Undo(); 
       }

    以上是对遥控器对命令的撤销,需要注意两点1、通过记住命令执行之前的状态,然后去恢复到原来的状态。2、在每次执行之后要记住执行的那个命令。也即记住命令和记住状态。

    除了一次执行一个命令和撤销一个命令,当然还可以一次执行多个命令。下面给出主要代码:

    public class MutlipleCommand : ICommand 
       { 
           ICommand[] commands; 
           ICommand[] backCommands; 
           public MutlipleCommand(ICommand[] commands) 
           { 
               this.commands = commands; 
               backCommands = new ICommand[commands.Length]; 
           }


           public void Excute() 
           { 
               for (int i = 0; i < commands.Length; i++) 
                { 
                  commands[i].Excute(); 
                  backCommands[i] = commands[i]; 
                } 
               
           }

           public void Undo() 
           { 
               for (int i = 0; i < commands.Length; i++) 
               { 
                   backCommands[i].Undo(); 
               } 
           } 
       }

    三、命令模式类图

    image

    四、总结

    命令模式主要通过中介Command实现了发出命令者和命令的执行者,也即Invoke类和Receiver的松耦合。本文先给出了命令模式的定义,通过吃饭的例子给出了使用命令模式实现遥控器设计思路,最后还提到了撤销命令和一个命令实现多个命令的做法。

    展开全文
  • 文章目录命令模式简介使用示例总结优缺点使用场景扩展联想学习 命令模式简介 命令模式(Command),将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可...
  • 命令模式(Command)

    千次阅读 2020-06-12 14:08:35
    18、命令模式(Command) 命令模式很好理解,举个例子,司令员下令让士兵去干件事情,从整个事情的角度来考虑,司令员的作用是,发出口令,口令经过传递,传到了士兵耳朵里,士兵去执行。这个过程好在,三者相互...
  • 一、意图  将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤消的操作。 ...(1)系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接...
  • java设计模式-命令模式

    万次阅读 2020-07-23 09:39:56
    java设计模式-命令模式 1 背景 & 定义 软件开发过程中,常常出现“方法请求者”与“方法实现者”间存在紧密耦合;不利于软件功能的扩展和维护;例如,想对行为进行“撤销、重做、记录”等处理就很不方便,命令...
  • linux vi & vim 插入 删除 修改 文本

    万次阅读 2018-01-16 15:28:17
    vi & vim 有编译模式与命令模式,下述光标移动操作均为命令模式下操作; 下文中以 ^ 符表示 Ctrl 键被按下,如 ^F 标示 Ctrl 与 F 键被同时按下; 下文中以 + 号表示按下某键后再按另外按键,如 z + Enter 表示先...
  • Linux文件编辑命令详细整理

    万次阅读 多人点赞 2016-02-25 09:57:37
    刚接触Linux,前几天申请了个免费...一、vi编辑器有3种基本工作模式首先需要知道vi编辑器有3种基本工作模式,分别是:命令模式、文本输入模式、和末行模式。第一:命令行模式:该模式是进入vi编辑器后的默认模式。任
  • JAVA设计模式--命令模式

    万次阅读 2019-03-26 09:52:06
    二、命令模式的适用性 三、命令模式的结构 四、命令模式的优点 五、认识命令模式 六、总结 一、什么是命令式 命令(Command)模式又叫作动作(Action)模式或事务(Transaction)模式,是一种对象的行为模式。将一个...
  • vim命令:编辑模式和命令模式

    万次阅读 2018-06-29 11:04:06
    vim命令:编辑模式和命令模式BatmanLinux0人评论474人阅读2018-01-04 19:07:16vim:编辑模式从一般模式进入编辑模式,只需你按一个键即可(i,I,a,A,o,O,r,R)。当进入编辑模式时,会在屏幕的最下一行出现“INSERT或...
  • (一)Linux中vi的复制粘贴命令

    万次阅读 2017-09-21 23:59:16
    vi编辑器有3种模式:命令模式、输入模式、末行模式。掌握这三种模式十分重要:  命令模式:vi启动后默认进入的是命令模式,从这个模式使用命令可以切换到另外两种模式,同时无论在任何模式下只要按一下[Esc]键都...
  • adb进入recovery 以及fastboot模式

    万次阅读 2018-05-06 10:10:04
    开机状态下:adb reboot fastboot 命令进入fastboot模式adb reboot recovery 命令行进recovery模式power + 音量下 recovery 模式关机状态下:power + 音量下 recovery 模式power + 音量上 fastboot 模式recovery...
  • 一、关于连接  FTP是仅基于TCP的服务,与众不同的是... FTP协议有两种工作方式:PORT模式和PASV模式,即主动模式和被动模式。  主动方式的连接过程:客户端从一个任意的非特权端口N(N>1024)向FTP服务器的命令
  • vim的visual模式

    万次阅读 2013-11-28 16:43:45
    在可视化模式下,可以对一个文本块的整体进行操作。例如,首先高亮选中一部分文本,然后用d命令删除这个文本块。可视化模式的好处在于,你可以在做改动之前,就看到操作将影响的文本。可视化模式可以分为以下三种: ...
  • 23种设计模式(10):命令模式

    万次阅读 多人点赞 2012-05-09 17:41:02
     顾名思义,命令模式就是对命令的封装,首先来看一下命令模式类图中的基本结构: Command类:是一个抽象类,类中对需要执行的命令进行声明,一般来说要对外公布一个execute方法用来执行命令。C
  • Linux - vi与vim使用与区别

    万次阅读 2020-01-26 22:20:54
    它们都是多模式编辑器,不同的是vim 是vi的升级版本,它不仅兼容vi的所有指令,而且还有一些新的特性在里面。 vim的这些优势主要体现在以下几个方面: ① 多级撤消 我们知道在vi里,按 u只能撤消上次命令,而在vim...
  • 在命令行中python的交互模式的退出

    万次阅读 2018-05-21 22:28:45
    先按Ctrl+z然后回车即可
  • 命令模式(Command Pattern)标签: 设计模式初涉描述性文字本节讲解的是行为型设计模式种的:命令模式,该模式非常简单, 就是用于行为请求者与行为实现者的解耦,举个简单的例子, 摆地摊与开店的流程:(例子参考...
1 2 3 4 5 ... 20
收藏数 1,199,082
精华内容 479,632
关键字:

命令模式