前言 根據上一篇的命令模式和在工作中遇到的一些實際情況,有了本篇文章,時時都是學習的一個過程,會在這個過程中發現許多好的模式或者是一種開發方式,今天寫出來的就是我工作中常用到的,自己感覺這種方式很優雅很漂亮,就自己試著實現了一下,可能原框架中不是這樣的,有許多不足之處還請大家指點。 需求 我還不清楚這種方式是模式還是框架開發中用到的技術,我暫且叫它為命令控制器吧。 命令控制器的主要功能就是獲取用戶提供的命令,然後來執行命令。 在這裡我把要執行的“命令”設計成一個函數,會對應著一個String類型的命令字符串,並且命令控制器是允許擴展的。 實現 首先我定義了一個屬性類,用於在擴展的命令類、或者命令函數上,只有一個CommandName屬性,作用於命令函數上的話,則代表命令名稱,如果是作用於類上面的話就代表命令類別的名稱,只是考慮可以作為一個分類,這部分在後面有講到,可以自定義實現。 復制代碼 1 /// <summary> 2 /// 命令所用代碼屬性類 3 /// </summary> 4 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)] 5 public class CommandAttribute : Attribute 6 { 7 private string commandName = null; 8 9 public string CommandName 10 { 11 get { return commandName; } 12 set { commandName = value; } 13 } 14 15 public CommandAttribute(string CommandName) 16 { 17 commandName = CommandName; 18 } 19 } 復制代碼 有了這個屬性類了,我們就要把它用起來,定義一些後面要用到的命令示例類, 復制代碼 1 /// <summary> 2 /// 示例:命令類以及命令函數,亦可當作為擴展 3 /// </summary> 4 [Command("Test")] 5 public class CommandTest : CommandMethod 6 { 7 [Command("MyCommandone")] 8 public object test(ICommandParameter commandparameter) 9 { 10 return null; 11 } 12 13 [Command("MyCommandone1")] 14 public object test1(ICommandParameter commandparameter) 15 { 16 return null; 17 } 18 19 [Command("MyCommandone2")] 20 public object test2(ICommandParameter commandparameter) 21 { 22 return null; 23 } 24 25 26 [Command("MyCommandone3")] 27 public object test3(ICommandParameter commandparameter) 28 { 29 return null; 30 } 31 32 [Command("MyCommandone4")] 33 public object test4(ICommandParameter commandparameter) 34 { 35 return null; 36 } 37 } 復制代碼 上面的示例代碼中可以看到CommandTest類繼承自CommandMethod類,而類裡面的一些函數的簽名也是一樣的,函數參數都為ICommandParameter接口類型,這就是擴展命令方法時要遵循的一些規范,定義的規范: 復制代碼 1 /// <summary> 2 /// 擴展命令函數的參數規范 3 /// </summary> 4 public interface ICommandParameter 5 { 6 7 } 8 9 /// <summary> 10 /// 擴展命令方法類基類 11 /// </summary> 12 public class CommandMethod 13 { 14 15 } 復制代碼 需要實現什麼都是可以自定義,比如說可以在ICommandParameter接口中定義個object類型的名稱為ContainerObject的屬性,意思就是容器屬性,可以在後面調用命令時,傳入實例參數時設置容器屬性的值,可以設置為任何你想設置的值到裡面,然後再在命令函數裡使用它,也可以根據抽象定義實現一個參數上下文對象專門用於處理擴展命令的,看需要自定義吧。 然後就是書寫一個命令控制器的代碼了,它呢主要負責把客戶端注冊進來的類型做一些處理,比如說 判斷類型、反射類型獲取函數命令以及函數信息、把命令函數轉換成委托、維護命令列表等等一些簡單的功能,還用到了一個CommandMethodActionDelegate類型的委托: 復制代碼 1 public delegate object CommandMethodActionDelegate(ICommandParameter commandParameter); 2 3 public class CommandController 4 { 5 private static CommandController _Instance = null; 6 7 public static CommandController Instance 8 { 9 get 10 { 11 if (_Instance == null) 12 { 13 _Instance = new CommandController(HostDevelopment.Instance); 14 } 15 return _Instance; 16 } 17 } 18 19 private HostDevelopment _CommandDevelopment = HostDevelopment.Instance; 20 21 public CommandController(HostDevelopment commandDevelopment) 22 { 23 _CommandDevelopment = commandDevelopment; 24 } 25 26 private Dictionary<string, CommandMethodActionDelegate> commandlist = new Dictionary<string, CommandMethodActionDelegate>(); 27 28 29 private List<string> _commandNames = null; 30 /// <summary> 31 /// 命令名稱集合 32 /// </summary> 33 public List<string> CommandNames 34 { 35 get 36 { 37 if (_commandNames == null) 38 { 39 GetCommandNames(); 40 } 41 return _commandNames; 42 } 43 } 44 45 private void GetCommandNames() 46 { 47 48 if (commandlist.Count > 0) 49 { 50 if (_commandNames == null) 51 { 52 _commandNames = new List<string>(); 53 } 54 foreach (string name in commandlist.Keys) 55 { 56 _commandNames.Add(name); 57 } 58 } 59 } 60 61 public bool RegisterCommand(object instance) 62 { 63 Type t = instance.GetType(); 64 CommandAttribute cmdatt = (CommandAttribute)Attribute.GetCustomAttribute(t, typeof(CommandAttribute), false); 65 if (cmdatt != null) 66 { 67 AddCommandToModel(instance); 68 return true; 69 } 70 else { return false; } 71 } 72 73 private void AddCommandToModel(object instance) 74 { 75 Type t = instance.GetType(); 76 MethodInfo[] methods = t.GetMethods(); 77 foreach (MethodInfo methodinfo in methods) 78 { 79 CommandAttribute cmdatt = (CommandAttribute)Attribute.GetCustomAttribute(methodinfo, typeof(CommandAttribute), false); 80 if (cmdatt != null) 81 { 82 83 CommandMethodActionDelegate commanddelegate = (CommandMethodActionDelegate)Delegate.CreateDelegate(typeof(CommandMethodActionDelegate), instance, methodinfo.Name); 84 commandlist.Add(cmdatt.CommandName, commanddelegate); 85 } 86 87 } 88 } 89 90 91 internal object Execute(string commandName, ICommandParameter commandParameter) 92 { 93 if (commandName == null) 94 { 95 throw new ArgumentNullException("commandName"); 96 } 97 if (!commandlist.ContainsKey(commandName)) 98 { 99 return new ArgumentNullException("不包含的命令,命令無效"); 100 } 101 CommandMethodActionDelegate cmdaction = commandlist[commandName]; 102 return cmdaction.Invoke(commandParameter); 103 } 104 } 復制代碼 在CommandController類型中,RegisterCommand()方法為注冊擴展命令到命令控制器,示例中的注冊方式為手動的傳入類型實例來注冊的,也可以把實現方式修改為在命令控制器啟動的時候獲取當前系統所有依賴項的程序集,獲取到所有符合類型規范的擴展命令類型,並且注冊到控制器中。 上面代碼中有說到的HostDevelopment類型,是我定義的一個宿主容器對象,用它來承載命令控制器,以及在這個系列的文章中,都是使用HostDevelopment來進行對控制器的承載,這是後話。現在來看一下HostDevelopment暫時的定義: 復制代碼 1 public class HostDevelopment 2 { 3 private static HostDevelopment _commandDevelopment = null; 4 5 public static HostDevelopment Instance 6 { 7 get 8 { 9 if (_commandDevelopment == null) 10 { 11 _commandDevelopment = new HostDevelopment(); 12 } 13 return _commandDevelopment; 14 } 15 } 16 17 private static ServiceContainer _serviceContainer = new ServiceContainer(); 18 19 private void SetServiceContainer(Type t,object instance) 20 { 21 if (instance == null) 22 { 23 throw new ArgumentNullException("instance"); 24 } 25 _serviceContainer.AddService(t, instance); 26 } 27 28 private object GetServiceContainer(Type t) 29 { 30 return _serviceContainer.GetService(t); 31 } 32 33 public CommandController CommandController 34 { 35 get 36 { 37 return (CommandController)GetServiceContainer(typeof(CommandController)); 38 } 39 } 40 41 public void Start() 42 { 43 SetServiceContainer(typeof(CommandController), new CommandController(this)); 44 } 45 46 public object Execute(string commandName, ICommandParameter commandParameter) 47 { 48 return CommandController.Execute(commandName, commandParameter); 49 } 50 } 復制代碼 看了這些了,應該就大致的明白了,不過現在這樣的代碼還是測試不了的,因為缺少一些實體。定義一個默認實現的命令參數規范: 復制代碼 1 public class CommandParameterCase:ICommandParameter 2 { 3 private string _StrText; 4 5 public string StrText 6 { 7 get { return _StrText; } 8 set { _StrText = value; } 9 } 10 } 復制代碼 然後再修改一下CommandTest類型中的第一個叫test的函數(對應的命令名稱為MyCommandone),函數名稱有點隨意,大家不要介意這些。 復制代碼 1 [Command("MyCommandone")] 2 public object test(ICommandParameter commandparameter) 3 { 4 if (commandparameter != null) 5 { 6 CommandParameterCase commandparametercase = commandparameter as CommandParameterCase; 7 return commandparametercase.StrText; 8 } 9 else { return null; } 10 } 復制代碼 這樣所需的都定義齊了。 我們再看一下調用代碼: 1 HostDevelopment.Instance.Start(); 2 HostDevelopment.Instance.CommandController.RegisterCommand(new CommandTest()); 3 var strtext = HostDevelopment.Instance.Execute("MyCommandone", new CommandParameterCase() { StrText = "test" }); 對了隨便是打個斷點或者是輸出strtext都可以看到它的值。這裡不作多的解釋了。