C#编程模式之扩展命令

前言

根据上一篇的命令模式和在工作中遇到的一些实际情况,有了本篇文章,时时都是学习的一个过程,会在这个过程中发现许多好的模式或者是一种开发方式,今天写出来的就是我工作中常用到的,自己感觉这种方式很优雅很漂亮,就自己试着实现了一下,可能原框架中不是这样的,有许多不足之处还请大家指点。

需求

我还不清楚这种方式是模式还是框架开发中用到的技术,我暂且叫它为命令控制器吧。

命令控制器的主要功能就是获取用户提供的命令,然后来执行命令。 在这里我把要执行的“命令”设计成一个函数,会对应着一个String类型的命令字符串,并且命令控制器是允许扩展的。

实现 首先我定义了一个属性类,用于在扩展的命令类、或者命令函数上,只有一个CommandName属性,作用于命令函数上的话,则代表命令名称,如果是作用于类上面的话就代表命令类别的名称,只是考虑可以作为一个分类,这部分在后面有讲到,可以自定义实现。

     /// <summary>
     /// 命令所用代码属性类
     /// </summary>
     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
     public class CommandAttribute : Attribute
     {
         private string commandName = null;
 
         public string CommandName
         {
             get { return commandName; }
             set { commandName = value; }
         }
 
         public CommandAttribute(string CommandName)
         {
             commandName = CommandName;
         }
     }

有了这个属性类了,我们就要把它用起来,定义一些后面要用到的命令示例类,

     /// <summary>
     /// 示例:命令类以及命令函数,亦可当作为扩展
     /// </summary>
     [Command("Test")]
     public class CommandTest : CommandMethod
     {
         [Command("MyCommandone")]
         public object test(ICommandParameter commandparameter)
         {
             return null;
         }
 
         [Command("MyCommandone1")]
         public object test1(ICommandParameter commandparameter)
         {
             return null;
         }
 
         [Command("MyCommandone2")]
         public object test2(ICommandParameter commandparameter)
         {
             return null;
         }
 
 
         [Command("MyCommandone3")]
         public object test3(ICommandParameter commandparameter)
         {
             return null;
         }
 
         [Command("MyCommandone4")]
         public object test4(ICommandParameter commandparameter)
         {
             return null;
         }
     }

上面的示例代码中可以看到CommandTest类继承自CommandMethod类,而类里面的一些函数的签名也是一样的,函数参数都为ICommandParameter接口类型,这就是扩展命令方法时要遵循的一些规范,定义的规范:

     /// <summary>
     /// 扩展命令函数的参数规范
     /// </summary>
     public interface ICommandParameter
     {
 
     }
 
     /// <summary>
     /// 扩展命令方法类基类
     /// </summary>
     public class CommandMethod
     {
 
     }

需要实现什么都是可以自定义,比如说可以在ICommandParameter接口中定义个object类型的名称为ContainerObject的属性,意思就是容器属性,可以在后面调用命令时,传入实例参数时设置容器属性的值,可以设置为任何你想设置的值到里面,然后再在命令函数里使用它,也可以根据抽象定义实现一个参数上下文对象专门用于处理扩展命令的,看需要自定义吧。

然后就是书写一个命令控制器的代码了,它呢主要负责把客户端注册进来的类型做一些处理,比如说 判断类型、反射类型获取函数命令以及函数信息、把命令函数转换成委托、维护命令列表等等一些简单的功能,还用到了一个CommandMethodActionDelegate类型的委托:

     public delegate object CommandMethodActionDelegate(ICommandParameter commandParameter);
     
     public class CommandController
     {
         private static CommandController _Instance = null;
 
         public static CommandController Instance
         {
             get
             {
                 if (_Instance == null)
                 {
                     _Instance = new CommandController(HostDevelopment.Instance);
                 }
                 return _Instance;
             }
         }
 
         private HostDevelopment _CommandDevelopment = HostDevelopment.Instance;
 
         public CommandController(HostDevelopment commandDevelopment)
         {
             _CommandDevelopment = commandDevelopment;
         }
 
         private Dictionary<string, CommandMethodActionDelegate> commandlist = new Dictionary<string, CommandMethodActionDelegate>();
 
 
         private List<string> _commandNames = null;
         /// <summary>
         /// 命令名称集合
         /// </summary>
         public List<string> CommandNames
         {
             get
             {
                 if (_commandNames == null)
                 {
                     GetCommandNames();
                 }
                 return _commandNames;
             }
         }
 
         private void GetCommandNames()
         {
 
             if (commandlist.Count > )
             {
                 if (_commandNames == null)
                 {
                     _commandNames = new List<string>();
                 }
                 foreach (string name in commandlist.Keys)
                 {
                     _commandNames.Add(name);
                 }
             }
         }
 
         public bool RegisterCommand(object instance)
         {
             Type t = instance.GetType();
             CommandAttribute cmdatt = (CommandAttribute)Attribute.GetCustomAttribute(t, typeof(CommandAttribute), false);
             if (cmdatt != null)
             {
                 AddCommandToModel(instance);
                 return true;
             }
             else { return false; }
         }
 
         private void AddCommandToModel(object instance)
         {
             Type t = instance.GetType();
             MethodInfo[] methods = t.GetMethods();
             foreach (MethodInfo methodinfo in methods)
             {
                 CommandAttribute cmdatt = (CommandAttribute)Attribute.GetCustomAttribute(methodinfo, typeof(CommandAttribute), false);
                 if (cmdatt != null)
                 {
 
                     CommandMethodActionDelegate commanddelegate = (CommandMethodActionDelegate)Delegate.CreateDelegate(typeof(CommandMethodActionDelegate), instance, methodinfo.Name);
                     commandlist.Add(cmdatt.CommandName, commanddelegate);
                 }
 
             }
         }
 
 
         internal object Execute(string commandName, ICommandParameter commandParameter)
         {
             if (commandName == null)
             {
                 throw new ArgumentNullException("commandName");
             }
             if (!commandlist.ContainsKey(commandName))
             {
                 return new ArgumentNullException("不包含的命令,命令无效");
             }
             CommandMethodActionDelegate cmdaction = commandlist[commandName];
             return cmdaction.Invoke(commandParameter);
         }
     }

在CommandController类型中,RegisterCommand()方法为注册扩展命令到命令控制器,示例中的注册方式为手动的传入类型实例来注册的,也可以把实现方式修改为在命令控制器启动的时候获取当前系统所有依赖项的程序集,获取到所有符合类型规范的扩展命令类型,并且注册到控制器中。

上面代码中有说到的HostDevelopment类型,是我定义的一个宿主容器对象,用它来承载命令控制器,以及在这个系列的文章中,都是使用HostDevelopment来进行对控制器的承载,这是后话。现在来看一下HostDevelopment暂时的定义:

     public class HostDevelopment
     {
         private static HostDevelopment _commandDevelopment = null;
 
         public static HostDevelopment Instance
         {
             get
             {
                 if (_commandDevelopment == null)
                 {
                     _commandDevelopment = new HostDevelopment();
                 }
                 return _commandDevelopment;
             }
         }
 
         private static ServiceContainer _serviceContainer = new ServiceContainer();
          
         private void SetServiceContainer(Type t,object instance)
         { 
             if (instance == null)
             {
                 throw new ArgumentNullException("instance");
             }
             _serviceContainer.AddService(t, instance);
         }
 
         private object GetServiceContainer(Type t)
         {
             return _serviceContainer.GetService(t);
         }
 
         public CommandController CommandController
         {
             get
             {
                 return (CommandController)GetServiceContainer(typeof(CommandController));
             }
         }
 
         public void Start()
         {
             SetServiceContainer(typeof(CommandController), new CommandController(this));
         }
 
         public object Execute(string commandName, ICommandParameter commandParameter)
         {
            return  CommandController.Execute(commandName, commandParameter);
         }
     }

看了这些了,应该就大致的明白了,不过现在这样的代码还是测试不了的,因为缺少一些实体。定义一个默认实现的命令参数规范:

     public class CommandParameterCase:ICommandParameter
     {
         private string _StrText;
 
         public string StrText
         {
             get { return _StrText; }
             set { _StrText = value; }
         }
     }

然后再修改一下CommandTest类型中的第一个叫test的函数(对应的命令名称为MyCommandone),函数名称有点随意,大家不要介意这些。

         [Command("MyCommandone")]
         public object test(ICommandParameter commandparameter)
         {
             if (commandparameter != null)
             {
                 CommandParameterCase commandparametercase = commandparameter as CommandParameterCase;
                 return commandparametercase.StrText;
             }
             else { return null; }
         }

这样所需的都定义齐了。 我们再看一下调用代码:

 HostDevelopment.Instance.Start();
 HostDevelopment.Instance.CommandController.RegisterCommand(new CommandTest());
 var strtext = HostDevelopment.Instance.Execute("MyCommandone", new CommandParameterCase() { StrText = "test" });

对了随便是打个断点或者是输出strtext都可以看到它的值。这里不作多的解释了。

最后加上对象关系图解吧,并不是什么标准的画法,有助于理解就行了。

END