【愚公系列】2021年12月 二十三种设计模式(十四)-命令模式(Command Pattern)-阿里云开发者社区

开发者社区> 愚公搬代码> 正文

【愚公系列】2021年12月 二十三种设计模式(十四)-命令模式(Command Pattern)

简介: 【愚公系列】2021年12月 二十三种设计模式(十四)-命令模式(Command Pattern)
+关注继续查看

文章目录

前言

一、命令模式(Command Pattern)

二、使用步骤

角色

示例

总结

优点

缺点

使用场景

前言

设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样。项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的核心解决方案,这也是它能被广泛应用的原因。


提示:以下是本篇文章正文内容,下面案例可供参考


一、命令模式(Command Pattern)

命令模式属于行为型模式,它尝试将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化。


在该设计模式中,请求以命令的形式包裹在对象中并传给调用对象。调用对象寻找可以处理该命令的合适的对象,并把该命令传给相应的对象请求执行。


二、使用步骤

角色

1、抽象命令(Command)


定义命令的接口,声明命令执行的方法;


2、具体命令(Concrete Command)


命令接口实现对象,需要维持对接收者的引用,并调用接收者的功能来完成命令要执行的操作;


3、接收者(Receiver)


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


4、调用者(Invoker)


要求命令对象执行请求,需要维持命令对象的引用,可以持有很多的命令对象。


示例

image.png

命名空间CommandPattern中包含Command基类、发票开具命令类CreateCommand、发票作废命令类CancelCommand、发票打印命令类PrintCommand、命令参数基类CommandArgs、发票开具命令参数类CommandArgs、发票作废命令参数类CancelArgs、发票打印命令参数类PrintArgs、接收者类Receiver和调用者类Invoker。本命尝试通过客户端调用不同的参数化发票命令来使调用者调用不同的功能。

public abstract class Command {

    protected Receiver _receiver = null;

    protected CommandArgs _commandArgs = null;

    public Command(Receiver receiver, CommandArgs commandArgs) {
        this._receiver = receiver;
        this._commandArgs = commandArgs;
    }

    public abstract void Action();

}

抽象命令基类,包含Action动作执行命令,并且维持对接受者和命令参数的引用。

public class CreateCommand : Command {

    public CreateCommand(Receiver receiver, CommandArgs commandArgs)
        : base(receiver, commandArgs) {

    }

    public override void Action() {
        _receiver.CommandArgs = _commandArgs;
        (_receiver as CreateReceiver)?.CreateInvoice();
    }

}

这是发票开具命令,由于基类维持了对调用者的引用,所以在Action方法中通过调用CreateInvoice方法来开具一张发票。

public class CancelCommand : Command {

    public CancelCommand(Receiver receiver, CommandArgs commandArgs)
        : base(receiver, commandArgs) {

    }

    public override void Action() {
        _receiver.CommandArgs = _commandArgs;
        (_receiver as CancelReceiver)?.CancelInvoice();
    }

}

这是发票作废命令,由于基类维持了对调用者的引用,所以在Action方法中通过调用CancelInvoice方法来作废一张发票。

public class PrintCommand : Command {

    public PrintCommand(Receiver receiver, CommandArgs commandArgs)
        : base(receiver, commandArgs) {

    }

    public override void Action() {
        _receiver.CommandArgs = _commandArgs;
        (_receiver as PrintReceiver)?.PrintInvoice();
    }

}

这是发票打印命令,由于基类维持了对调用者的引用,所以在Action方法中通过调用PrintInvoice方法来打印一张发票。

public class CommandArgs {

    public string InvoiceType { get; set; }

}
public class CreateArgs : CommandArgs {

    public DateTime BillingDate { get; set; }

}
public class CancelArgs : CommandArgs {

    public string InvoiceCode { get; set; }
    public int InvoiceNumber { get; set; }
    public string CancelReason { get; set; }
    public string CancelMan { get; set; }
    public DateTime CancelDate { get; set; }

}
public class PrintArgs : CommandArgs {

    public string InvoiceCode { get; set; }
    public int InvoiceNumber { get; set; }

}

参数化的命令参数基类CommandArgs和它的3个具体实现类。实际开发过程中可以将参数化命令信息封装在具体命令类中,本例为了更好的扩展性,将参数化命令信息抽象出来。

public class Invoker {

    private Command _command = null;

    public Invoker(Command command) {
        this._command = command;
    }

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

}

调用者类Invoker,实际开发中这个应为具体的调用类。例如我们需要从MQ获取实时数据,并根据从MQ获取到的JSON数据来处理不同的命令,那么这个调用者类应该为MQ所在的管理类(假如名为ActiveMQManager)。这时我们需要在ActiveMQManager类中维持对命令基类的引用,并在收到不同的JSON数据时解析出相应命令和命令参数信息,然后执行命令中的Action方法。

public abstract class Receiver {

    public CommandArgs CommandArgs { get; set; }

    protected const string LINE_BREAK =
        "-------------------------" +
        "-------------------------";
    //文章排版需要,故折成2行

}
public class CreateReceiver : Receiver {
 
    public void CreateInvoice() {
        var args = CommandArgs as CreateArgs;
        if (args == null) throw new InvalidOperationException();
        Console.WriteLine("Create Invoice!");
        Console.WriteLine(
            $"InvoiceType is {args.InvoiceType},{Environment.NewLine}" +
            $"BillingDate is {args.BillingDate.ToString("yyyy-MM-dd HH:mm:ss")}!");
        Console.WriteLine(LINE_BREAK);
    }
 
}
public class CancelReceiver : Receiver {
 
    public void CancelInvoice() {
        var args = CommandArgs as CancelArgs;
        if (args == null) throw new InvalidOperationException();
        Console.WriteLine("Cancel Invoice!");
        Console.WriteLine(
            $"InvoiceCode is {args.InvoiceCode},{Environment.NewLine}" +
            $"InvoiceNumber is {args.InvoiceNumber},{Environment.NewLine}" +
            $"InvoiceType is {args.InvoiceType},{Environment.NewLine}" +
            $"CancelReason is {args.CancelReason},{Environment.NewLine}" +
            $"CancelMan is {args.CancelMan},{Environment.NewLine}" +
            $"CancelDate is {args.CancelDate.ToString("yyyy-MM-dd HH:mm:ss")}!");
        Console.WriteLine(LINE_BREAK);
    }
 
}
public class PrintReceiver : Receiver {
 
    public void PrintInvoice() {
        var args = CommandArgs as PrintArgs;
        if (args == null) throw new InvalidOperationException();
        Console.WriteLine("Print Invoice!");
        Console.WriteLine(
            $"InvoiceCode is {args.InvoiceCode},{Environment.NewLine}" +
            $"InvoiceNumber is {args.InvoiceNumber},{Environment.NewLine}" +
            $"InvoiceType is {args.InvoiceType}!");
        Console.WriteLine(LINE_BREAK);
    }
 
}

接收者基类Receiver和它的3个具体接收者类,需要维持对命令参数基类的引用,以便我们可以获取相应信息。接收者基类并不是命令模式必须的,但考虑到里氏替换原则和开闭原则,我们引入接收者基类并在不同的实现类里解耦不同的命令操作。

public class Program {

    private static Receiver _receiver = null;

    public static void Main(string[] args) {
        _receiver = new CreateReceiver();
        Command command = new CreateCommand(
            _receiver, new CreateArgs {
                InvoiceType = "004",
                BillingDate = DateTime.UtcNow
            });

        var invoker = new Invoker(command);
        invoker.Execute();

        _receiver = new CancelReceiver();
        command = new CancelCommand(
            _receiver, new CancelArgs {
                InvoiceCode = "310987289304",
                InvoiceNumber = 34156934,
                InvoiceType = "007",
                CancelReason = "Invoice missing!",
                CancelMan = "Iori",
                CancelDate = DateTime.UtcNow
            });

        invoker = new Invoker(command);
        invoker.Execute();

        _receiver = new PrintReceiver();
        command = new PrintCommand(
            _receiver, new PrintArgs {
                InvoiceCode = "310987289304",
                InvoiceNumber = 34156934,
                InvoiceType = "026"
            });

        invoker = new Invoker(command);
        invoker.Execute();

        Console.ReadKey();
    }

}

以上是为了测试本案例所编写的代码,通过不同的命令并提供额外的参数化命令信息来执行不同的功能。以下是这个案例的输出结果:

Create Invoice!
InvoiceType is 004,
BillingDate is 2018-07-19 05:34:45!
--------------------------------------------------
Cancel Invoice!
InvoiceCode is 310987289304,
InvoiceNumber is 34156934,
InvoiceType is 007,
CancelReason is Invoice missing!,
CancelMan is Iori,
CancelDate is 2018-07-19 05:34:45!
--------------------------------------------------
Print Invoice!
InvoiceCode is 310987289304,
InvoiceNumber is 34156934,
InvoiceType is 026!
--------------------------------------------------

总结

优点

1、降低对象之间的耦合度,通过参数化的命令信息来驱动程序的运行;

2、新的命令可以很容易地加入到系统中;

3、可以比较容易地设计一个组合命令;

4、调用同一方法实现不同的功能。


缺点

使用命令模式可能会导致某些系统有过多的具体命令类,导致子类膨胀。


使用场景

1、系统需要将请求调用者和请求接收者解耦,使得调用者和接收者不直接交互;

2、系统需要在不同的时间指定请求、将请求排队和执行请求;

3、系统需要支持命令的撤销(Undo)操作和恢复(Redo)操作。


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
IOS设计模式第十篇之命令行设计模式
命令行设计模式: 命令设计模式将一个请求或行动作封装为对象。这个封装请求比原始的请求要灵活并且可以在对象之前被传递,存储,动态修改或者放进队列里面。苹果 苹果公司实现这种模式使用Target-Action机制和Invocation。
875 0
设计模式 ( 十三 ) 命令模式Command(对象行为型)
<p><strong><a target="_blank" href="http://blog.csdn.net/hguisu/article/details/7549895" style="color:rgb(51,102,153); text-decoration:none; font-family:'Microsoft YaHei'; line-height:30px"><span
1193 0
设计模式--命令模式(Command)
基本概念:   Command模式也叫命令模式 ,是行为设计模式的一种。Command模式通过被称为Command的类封装了对目标对象的调用行为以及调用参数,命令模式将方法调用给封装起来了。 命令模式的几个角色: Command: 抽象命令类 ConcreteCommand: 具体命令类 Invoker: 调用者 Receiver: 接收者 Client:客户类 命令模式的优缺点:   优点 1. 降低了系统耦合度 2. 新的命令可以很容易添加到系统中去。
1128 0
PHP设计模式——命令模式
声明:本系列博客参考资料《大话设计模式》,作者程杰。             命令模式:在软件系统中,“行为请求者”与“行为实现者”通常呈现一种“紧耦合”。
822 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
6491 0
+关注
愚公搬代码
该博客包括:.NET、前端、IOS、Android、Linux、物联网、网络安全、python、大数据等相关使用及进阶知识。查看博客过程中,如有任何问题,皆可随时沟通。
150
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《2021云上架构与运维峰会演讲合集》
立即下载
《零基础CSS入门教程》
立即下载
《零基础HTML入门教程》
立即下载