架構演化學習思考(3)
接上一篇我們繼續對命令模式進行學習。
在這節內容中,我們聊一下經典的命令模式,還記得上一篇文章開頭我們實現的簡單的命令模式嗎?來看程式碼,非常簡單易解。
public interface ICommand
{
void Execute();
}
public class PlayMusicCommand : ICommand
{
public void Execute()
{
Debug.Log("你說家是唯一的城堡,隨著稻香一路奔跑~");
}
}
var Start()
{
var command = new PlayMusicCommand();
command.Execute();
}
以上最簡的命令模式中,我們可以分離出一些角色。
ICommand:介面 對應經典命令模式中的 Command的角色
PlayMusicCommand:類 繼承介面,是介面的具體實現,對應經典命令模式中的ConcreteCommand角色。
Start方法:是命令的發出者,對應經典命令模式中的Invoker角色
由此我們提煉實現經典命令模式的三個角色。
經典命令模式的實現完善
還差一個Receiver,即命令的接收者或執行者,組成經典命令模式的四種(也有說五種)角色:
- Command:抽象命令(命令介面)
- ConcreteCommand:具體命令
- Invoker:命令的呼叫者,發起者,觸發者。
- Receiver:命令的接收者,被Command訪問和操作。
- 客戶端:建立具體命令物件並設定其接收者,將命令物件交給呼叫者執行(如果涉及到5部分的話)
我們還以商品購買為例,用以上涉及到的角色方式來實現購買貨品這樣一個操作。
namespace TestCommand
{
//售貨員 對應Receiver
public class Salesperson
{
public void SellGoods(int id,int count)
{
for (int i = 1; i <= count; i++)
{
Debug.Log($"編號為{id}的商品售出1件!");
}
Debug.Log($"編號為{id}的商品總售出{count}件!");
}
}
//命令介面 對應Command
public interface Command
{
void Execute();
}
//具體實現 對應ConcreteCommand 具體命令
public class BuyCommand : Command
{
public Salesperson salesPerson;
public int goodsId;
public int count;
public void Execute()
{
salesPerson.SellGoods(goodsId,count);
}
}
//觸發者 命令的傳送者
//顧客
public class Customer
{
private List<Command> mCommands = new List<Command>();
public void AddCommand(Command command)
{
mCommands.Add(command);
}
//觸發命令
public void triggerCommands()
{
mCommands.ForEach(command=>command.Execute());
mCommands.Clear();
}
}
//客戶端角色
void Start()
{
var customer = new Customer();
var salesperson = new Salesperson();
//2 號商品購買五件 的命令
customer.AddCommand(new BuyCommand()
{
salesPerson = salesperson,
goodsId = 2,
count = 5
});
//讓顧客發出購買命令
customer.triggerCommands();
}
}
其大體思路如下:對應著經典命令模式的五部分
終於將經典模式的五部分實現完整了,命令模式梳理到這裡差不多結束了,那麼這對架構設計有啥啟發和應用思考呢?
當然有。
在專案中,我們對位於底層部分的資料模組訪問時就可以應用命令模式,Recever為底層的System或者資料Model,ConCreteCommand為對應的具體操作(對資料進行查和改、解鎖成就係統的成就),而觸發器Involver則是架構,也就是說整個專案的依賴關係的總掌控者,而客戶端則對應表現層的控制邏輯。
不知道有沒有對”架構“這個觸發者的認知有沒有更加具體一些呢?
筆者是這樣理解”架構“的身份的,好比是一個交換機接線員,當我們需要和朋友電話時候,則要拿起自己這邊的話筒傳呼接線員,告訴TA自己朋友的電話機號,然後接通之後,完成我們對朋友交流的需求。當然這個例子不一定十分準確,但很大程度上讓大家對”架構“的認識沒有那麼抽象。
好,在回到經典模式,來看一看此模式有什麼好處。
命令模式的好處
好處之一是將Invoker和Receiver完全解耦。
那這個功能好像觀察者模式也可以實現吧,那麼和觀察者模式對比有些不同呢?答案還在命令本身,命令除了將invoker和receiver解耦,還可以進行自由擴充套件。我可以購買、也可以退貨,也可以更換商品等等一些操作。當然從Invoker這邊可以對命令進行儲存(使用堆或者棧或者List等容器),進而可以實現回退復原、行為樹等功能。
關於命令模式的另一點思考:
我們將視角聚焦在命令模式的Command中,也就是結構圖上的”訂單模板“。有了模板就好進行擴充,這裡簡單聊聊,命令模式中的開閉原則。
命令模式中的開閉原則
開閉原則大家比較熟悉:
開閉原則:一個類應當對擴充套件開放、對修改關閉
當一個結構模組或系統開發成型之後,除非遇到一些bug或者功能缺陷,否則不應該對結構模組或者系統進行修改,這就是對修改關閉。而需要新增加功能或者擴充時候,可以透過擴充套件的方式來新增一些功能,也就是對擴充套件開放。
而根據命令模式中的各個角色,在擴充功能時候有著不同的準則和作用:
- Invoker:關閉修改
- Recever:關閉內部修改
- Command: 擴充的標準
- ConcreteCommand :開放實現的擴充
筆者的專案功底尚淺,只是簡單的聊一下涉及到開閉原則,願大家多寫程式碼多實踐,
慢慢將這些思考在實際專案中有所應用和體驗,逐步提高自己的程式設計和架構設計能力。
好,關於命令模式我們就聊到這裡了,接下來還會繼續更新此係列內容,謝謝各位與我一起思考和體悟!