夸克之书

  • 首页
  • 科普
  • 笔记
  • .NET/C#
  • 物联网
  • 算法
  • Linux
  • 树莓派
夸克之内,别有洞天
  1. 首页
  2. .NET/C#
  3. 正文

23种常见的设计模式(9):命令模式

2020-02-08 2753点热度 0人点赞 0条评论

什么是命令模式?命令模式是将一个请求(命令)封装成一个对象,把不同的请求进行参数化,对请求排队或者记录请求日志,可以提供命令的撤销和恢复功能。可能理解起来有些困难,不过不用着急,我们一起通过下面这个例子来学习命令模式。

但本篇主要内容是了解命令模式的结构,命令模式的撤销和恢复功能暂且不表(比较复杂,可以参考数据库日志恢复)。

以抗日神剧中的一个营为例,一般来说一个营中分为多个连队,他们可能是同种性质的兵种也也能不是同种性质的兵种。(具体的划分我也不清楚,但是看日神剧里面确实是这么演的,咋们也不用去追究具体是不是这样的,理解这个例子中采用的设计模式才是最重要的)。

假如你是营长,你的营下面分为三个连队,分别是突击兵连、炮兵连和炊事班。具体每个连队的作用就不多说了。作为营长,肯定要将自己的命令传达下去,然后再由各个连队去执行。现在有两个个指令,第一个是攻击命令,炮兵轰炸敌方阵地,然后突击兵占领敌方阵地。第二个是战斗结束之后,生火吃饭。

作为本例子简化之后连队有两个动作,第一个Work,做自己的本质工作。比如炮兵负责炮击,突击兵冲锋占领阵地,炊事班煮饭。然后有一个共同的动作,就是吃饭,毕竟人是铁饭是钢。下面是三个连队的代码:

ITeam接口:

interface ITeam
{
    void Work();

    void Eating();
}

炮兵(Artillery):

/// <summary>
/// 炮兵
/// </summary>
class Artillery : ITeam
{
    public void Eating()
    {
        Console.WriteLine("炮兵在吃饭。");
    }

    public void Work()
    {
        Console.WriteLine("炮兵开始攻击。");
        Console.WriteLine("我是炮兵,我正在攻击对方阵地...");
        Console.WriteLine("炮兵攻击结束。");
    }
}

突击兵(Assault):

/// <summary>
/// 突击兵
/// </summary>
class Assault : ITeam
{
    public void Eating()
    {
        Console.WriteLine("突击兵在吃饭。");
    }

    public void Work()
    {
        Console.WriteLine("突击兵开始攻击。");
        Console.WriteLine("炮兵已经占领对方阵地,战斗取得胜利。");
    }
}

炊事班(Cooking):

/// <summary>
/// 炊事班
/// </summary>
class Cooking : ITeam
{
    public void Eating()
    {
        Console.WriteLine("炊事班在吃饭(我们虽然做饭,但我们也要吃饭)。");
    }

    public void Work()
    {
        Console.WriteLine("炊事班开始做饭。");
        Console.WriteLine("炊事班已经将饭做好了,可以开饭了...");
    }
}

目前的情况是通讯员负伤了,只能由营长亲自挨个去通知(非命令模式)情况下的实现。

class Program
{
    static void Main(string[] args)
    {
        //非命令模式情况下
        //攻击命令
        Console.WriteLine("非命令模式:");
        Artillery _artillery = new Artillery();  //炮兵
        Assault _assault = new Assault();  //突击兵

        _artillery.Work();  //炮兵攻击
        _assault.Work();  //突击兵占领阵地

        Console.ReadKey(false);
    }
}

可以看到,下达一个攻击命令就需要营长每个连去跑一趟(电话联系一次)。这样的效率肯定是不允许的,战场上面战机稍纵即逝,一刻也不能耽搁。光单个命令就是这样的,那源源不断的指挥命令怎么办?

所以就引入了命令模式。我们将不同的命令的封装起来,交由通讯员统一传达,比如攻击命令是什么?攻击命令就是炮兵炸了突击兵上!吃饭命令是什么?吃饭命令就是炊事班做了饭,全营就吃饭。简而言之就是把命令抽象出来,我只管发布命令即可。

定义一个ICommand的接口,接口中就包含了一个方法:Excute。具体的命令继承ICommand接口之后,实现Excute方法,作为该命令执行动作。

ICommand接口:

interface ICommand
{
    void Execute();
}

攻击命令(AttackCommand):

/// <summary>
/// 攻击命令
/// </summary>
class AttackCommand : ICommand
{
    /// <summary>
    /// 炮兵
    /// </summary>
    private readonly Artillery _artillery = new Artillery();

    /// <summary>
    /// 突击兵
    /// </summary>
    private readonly Assault _assault = new Assault();

    public void Execute()
    {
        //炮兵攻击
        _artillery.Work();

        //突击兵占领阵地
        _assault.Work();
    }
}

吃饭命令(EatCommand):

class EatCommand : ICommand
{
    /// <summary>
    /// 炮兵
    /// </summary>
    private readonly Artillery _artillery = new Artillery();

    /// <summary>
    /// 突击兵
    /// </summary>
    private readonly Assault _assault = new Assault();

    /// <summary>
    /// 炊事班
    /// </summary>
    private readonly Cooking _cooking = new Cooking();

    public void Execute()
    {
        //炊事班做饭
        _cooking.Work();
        //吃饭
        _artillery.Eating();
        _assault.Eating();
        _cooking.Eating();
    }
}

然后我们新指派一个通讯员(接收者),让通讯员去传达营长的命令。

/// <summary>
/// 通讯员
/// </summary>
class Invoker
{
    private ICommand _command = null;

    public Invoker(ICommand command)
    {
        if (command == null)
        {
            throw new Exception("指令不能为空。");
        }
        _command = command;
    }

    public void Action()
    {
        _command.Execute();
    }
}

通讯员作为一个接受者,使用了有参构造函数,参数就是营长的命令。当收到营长的命令之后,通过Action动作来让命令中具体的执行者执行命令。

%title插图%num
Demo参考类图

此时营长下发命令是这样的:

class Program
{
    static void Main(string[] args)
    {
        //命令模式情况下
        Console.WriteLine("n命令模式:");
        Invoker invoker = null;

        invoker = new Invoker(new AttackCommand());  //攻击命令
        invoker.Action();

        invoker = new Invoker(new EatCommand());   //吃饭命令
        invoker.Action();

        Console.ReadKey(false);
    }
}

现在只需要构建命令,然后通过通讯员下发执行即可。看看执行结果:

%title插图%num
命令模式执行结果

可以看到,在命令模式中主要分为三个角色:

(1)接收者

也就是连队,负责做具体的事情,比如炮兵连负责炮击敌方阵地。

(2)命令(封装的指令)

把要执行的命令都封装起来,作为参数传递给Invoker调用执行。

(3)调用者(传令兵)。

也就是Invoker,负责接收并执行指令。

命令模式很简单,也在项目中使用的非常频繁。命令模式把调用者和接收者分开了,从上面的例子可以看出,调用者和接收者并没有任何依赖关系,要调用具体的功能时,只需要执行Excute方法即可,通过封装的命令,调用者不需要去了解执行这个命令到底做了哪些工作。就像传令兵并不需要了解炮兵是使用哪种炮弹攻击敌方阵地的。

%title插图%num
命令模式通用类图

但同时,也可以看出命令模式的一个致命缺陷:Command膨胀的问题。如果有N个命令就需要定义N个Command。这点在使用过程中也是需要特别考虑的,可能也需要结合模板方法模式来解决这个问题。

扩展

1、命令模式高层次调用人员是否需要知道接收者?

在上面的例子中,我们是把高层调用人员(营长)和接收人员(连队)隔离开了的。但是实际上,营长是可以直接命令连队的。同样,在程序实现中也可以让高层地模式制定接收者,这就需要在具体项目中去做符合项目实际的考虑了。

2、命令模式日志记录功能

既然将命令进行了封装,那就能很详细的记录日志了。这个实现起来很容易,就不做过多讨论了。

3、命令模式的撤销和恢复

命令模式撤销和恢复功能非常复杂,本篇主要讨论命令模式的实现,想要了解跟多就需要自己去不断探索和学习。

4、命令模式与其他模式的结合

其实任意一个设计模式都可以去其他设计模式进行搭配,关键在于是否合适,是否符合项目需求等。比如命令模式与模板方法模式进行结合,可以很好的解决Command膨胀问题。

Demo下载:点击下载

参考

1.秦小波. 设计模式之禅. 机械工业出版社
2.https://www.cnblogs.com/guyun/p/6180377.html

本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可
标签: 暂无
最后更新:2020-12-13

afirefish

这个人很懒,什么都没留下

打赏 点赞
< 上一篇
下一篇 >

文章评论

您需要 登录 之后才可以评论
放松一下
https://www.quarkbook.com/wp-content/uploads/2021/05/凤凰传奇-海底(Live).flac
分类
  • .NET/C#
  • Linux
  • 树莓派
  • 物联网
  • 科普
  • 笔记
  • 算法
  • 默认
最新 热点 随机
最新 热点 随机
维持宇宙的四种“力量”——关于四大基本力 MinGW图形安装界面里面没有mingw32 make.exe解决办法 Windows Server 2022安装Intel I225-V/I226-V驱动 System.Text.Json与Newtonsoft.Json Json序列化与反序列化性能对比 R86S散热改造 Windows移除多余输入法'Unknown Locale (qaa-Latn)'
Windows Server 2022安装Intel I225-V/I226-V驱动MinGW图形安装界面里面没有mingw32 make.exe解决办法维持宇宙的四种“力量”——关于四大基本力
使用淘宝npm以及安装cnpm 获取访问IP信息接口(暂不开放) 基于CURL的HTTP请求代码(C语言) ESXi 6.7U3离线驱动封装 23种常见的设计模式(1):单例模式 OpenWrt x86安装Frpc
最近评论
afirefish 发布于 4 个月前(11月28日) 非常感谢,非常棒!
》随缘《 发布于 4 个月前(11月20日) 最新【一键处理】方法: https://github.com/MrXhh/VSTools/rele...
管理员 发布于 9 个月前(06月22日) emmmm....服务器好一点???
wking 发布于 10 个月前(05月23日) 请问贵博客是怎么优化的,网页响应速度非常快。我博客同样的WordPress和kratos主题,但点一...
去月球 发布于 1 年前(01月17日) 如果使用CSI的摄像头应该怎么修改命令呢
书签
  • 打赏
  • 毒鸡汤
  • 米店
  • 金鱼直播间

COPYRIGHT © 2022 quarkbook.com. ALL RIGHTS RESERVED.

Theme Kratos Made By Seaton Jiang

蜀ICP备15036129号-9

登录
注册|忘记密码?