命令模式屬于行為型模式,它嘗試将一個請求封裝成一個對象,從而使您可以用不同的請求對客戶進行參數化。
在該設計模式中,請求以命令的形式包裹在對象中并傳給調用對象。調用對象尋找可以處理該命令的合适的對象,并把該命令傳給相應的對象請求執行。
角色1、抽象命令(Command)
定義命令的接口,聲明命令執行的方法;
2、具體命令(Concrete Command)
命令接口實現對象,需要維持對接收者的引用,并調用接收者的功能來完成命令要執行的操作;
3、接收者(Receiver)
真正執行命令的對象。任何類都可能成為一個接收者,隻要它能夠實現命令要求實現的相應功能;
4、調用者(Invoker)
要求命令對象執行請求,需要維持命令對象的引用,可以持有很多的命令對象。
示例
命名空間CommandPattern中包含Command基類、發票開具命令類CreateCommand、發票作廢命令類CancelCommand、發票打印命令類PrintCommand、命令參數基類CommandArgs、發票開具命令參數類CommandArgs、發票作廢命令參數類CancelArgs、發票打印命令參數類PrintArgs、接收者類Receiver和調用者類Invoker。本命嘗試通過客戶端調用不同的參數化發票命令來使調用者調用不同的功能。
namespace CommandPattern
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)操作。
,更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!