设计之美——命令模式(二)

来源:互联网 发布:网络与信息安全专业 编辑:程序博客网 时间:2024/05/22 18:22

  也许你看过第一篇,也许没有,如果没有我推荐你看了继续看这个,因为这是接在那个下面的。这里将对上一篇进行完善,实现一个设想中他该那么工作的一组类。来看代码吧……

 

    //CMDManager.cs
    /// <summary>
    
/// 这里叫他命令管理器
    
/// 用于对命令对象的管理和传递
    
/// 设计之美——命令模式(二)
    
/// 也许你看过第一篇,也许没有,如果没有我推荐你看了继续看这个,因为这是接在那个下面的。
    
/// 上一篇实现了一些乱七八糟的demo 我是这么觉得,很显然我不满意这样的结果
    
/// 我希望以一个更简单的方式来实现我的要求,我希望我只要通过一个属性设置所有可支持的命令
    
/// 比如上一demo中 启动命令 
    
/// 在上面增加一个属性[CmdAcctib("启动")]
    
/// 然后我们需要一个关于启动的命令
    
/// 嗯 当初空余的属性对象看起来用得到了
    
/// 我们得需要一些东西来告诉外部的对象如何进行命令执行
    
/// 
    
/// 新 Demo 中我们仅对无参数的方法和公开属性进行操作
    
/// 是觉得有限制么?嗯? 你觉得发射有什么做不到的么?没有!是的没有!
    
/// 
    
/// 好吧我们先来设计关于一个简单的反射
    
/// 
    
/// 请去往CmdConfig.cs->(希望你还记得我的文章怎么看:)
    
/// 
    
/// ->包装器
    
/// 终于快完成了 意味着你不用再跳任何一次了!
    
/// 放心吧我保证!!
    
/// 
    
/// 来看看这个类的功能设计
    
/// </summary>

    class CMDManager
    
{

        
//需要一个清单来进行操作
        
//这里使用了一个字典
        /// <summary>
        
/// 命令对象字典
        
/// </summary>

        Dictionary<Type, List<ICmdObj>> _CmdObjDic;

        ICmdObjFactory _CmdObjFactory;
        
static CMDManager _Instance;
        
//很显然这个类只会生成一次
        
//所以使用单件模式
        public static CMDManager Instance
        
{
            
get
            
{
                
if (_Instance == null)
                
{
                    _Instance 
= new CMDManager();
                }

                
return CMDManager._Instance;
            }

            
set { CMDManager._Instance = value; }
        }


        
private CMDManager()
        
{
            _Init();
        }



        
//然后我们需要一个初始化函数
        
//这里需要做些什么呢?
        
//需要遍历一个程序集,并且生成可使用的对象和命令列表
        void _Init()
        
{
            _CmdObjDic 
= new Dictionary<Type, List<ICmdObj>>();
            _CmdObjFactory 
= new ClassCmdObjFactory();//这里使用类包装工厂

            
foreach (Type t in Assembly.GetExecutingAssembly().GetTypes())
            
{
                
bool _isReceiver = false;
                
foreach (Type Inter in t.GetInterfaces())
                
{
                    
if (Inter == typeof(IReceiver))
                    
{
                        _isReceiver 
= true;
                        
break;
                    }

                }

                
if (!_isReceiver) continue;
                
//判断是否为接受者接口
                
//遍历属性 和 方法
                List<ICmdObj> result = new List<ICmdObj>();

                
foreach (MemberInfo var in t.GetMembers())
                
{
                    
foreach (object attrib in var.GetCustomAttributes(true))
                    
{
                        
if (attrib is CmdAttrib)
                        
{
                            result.Add(_CmdObjFactory.Creat(var, attrib 
as CmdAttrib));
                        }

                    }


                }

                
//foreach (PropertyInfo var in t.GetProperties())
                
//{
                
//    foreach (object attrib in var.GetCustomAttributes(true))
                
//    {
                
//        if (attrib is CmdAttrib)
                
//        {
                
//            result.Add(_CmdObjFactory.Creat(var, attrib as CmdAttrib));
                
//        }
                
//    }

                
//}

                _CmdObjDic.Add(t, result);

                
//生产完毕
                
//接着干嘛?
                
//当然是调用咯!
                
//很显然我们不希望手动去写没一个命令 那么就让代码生成器给写一个把
                
//首先得看一下关于模板的设计
                
//不过如果带参数就比较麻烦了 T T
                
//跳往模板->
                
//(很抱歉 又让您跳了....:p

            }




        }


        
public ICmdObj GetCmd(IReceiver receiver, string cmdname)
        
{
            Type rtype 
= receiver.GetType();

            
if (_CmdObjDic.ContainsKey(rtype))
            
{
                
foreach (ICmdObj var in _CmdObjDic[rtype])
                
{
                    
if (var.Name == cmdname)
                    
{
                        
//这里将进行接收者的赋值 但是很显然一个问题就是无法进行 直接赋值的
                        
//但是我们查找到的却相当于一个模板 重新制作一份命令
                        return _CmdObjFactory.Creat(var, receiver);
                    }

                }

            }


            
//查找基类是接收着的类
            foreach (KeyValuePair<Type, List<ICmdObj>> var in _CmdObjDic)
            
{
                
if (var.Value.Count > 0)
                
{
                    
if (var.Key.BaseType == rtype)
                    
{
                        
foreach (ICmdObj cmdobj in var.Value)
                        
{
                            
if (cmdobj.Name == cmdname)
                            
{
                                
//这里将进行接收者的赋值 但是很显然一个问题就是无法进行 直接赋值的
                                
//但是我们查找到的却相当于一个模板 重新制作一份命令

                                
return _CmdObjFactory.Creat(cmdobj, Activator.CreateInstance(var.Key, receiver) as IReceiver);
                            }

                        }

                    }

                }

            }


            
throw new Exception("未找到有效的命令!");
        }

        
//->模板
        
//先不管带参数的
        
//演示而已 嘿嘿 就让我偷回懒吧!
        public class classname : CmdCilent
        
{
            
//构造
            public classname(IReceiver receiver)
                : 
base(receiver)
            
{
            }


            
public void cmdname()
            
{
                SendCmd(
new CmdMessage(CMDManager.Instance.GetCmd(this._Receiver, "cmdname")));
            }

        }


        
/// <summary>
        
/// 代码生成器
        
/// </summary>
        
/// <param name="name"></param>

        public void CsFileCreat()
        
{
            
string head = @"

            class C{0} : CmdCilent
            {            
                public C{0}(IReceiver receiver)
                : base(receiver)
                {
                    
                }

                {1}
            }

";
            
string body = @"
                        public void {0}()
            {
                SendCmd(new CmdMessage(CMDManager.Instance.GetCmd(this._Receiver, {1})));
            }
            
";
            StringBuilder sb 
= new StringBuilder();
            sb.Append(
"namespace DS_CMD.Command{");
            
foreach (KeyValuePair<Type, List<ICmdObj>> var in _CmdObjDic)
            
{
                
if (var.Value.Count > 0)
                
{
                    StringBuilder sbody 
= new StringBuilder();
                    
foreach (ICmdObj cmd in var.Value)
                    
{
                        sbody.Append(body.Replace(
"{1}",  """ + cmd.Name + """).Replace("{0}",cmd.Name));
                    }

                    sb.Append(head.Replace(
"{0}", var.Key.Name).Replace("{1}", sbody.ToString()));
                }

            }

            sb.Append(
"}");

            System.IO.File.WriteAllText(
"mycmd.cs", sb.ToString(),System.Text.Encoding.Default);
        }


        
//done!
        
//测试下
        
//运行一次 将在目录下生成一个mycmd.cs
        
//然后拷贝进项目 再 测试一下

    }
    /// <summary>
    
/// 命令类型
    
/// </summary>

    //internal enum CmdType
    
//{
    
//    属性,
    
//    方法,
    
//    事件
    
//}

    
//->CmdConfig.cs
    
//这里我们将进行命令对象的配置工作
    
//我把CmdType 挪到了这里方便查看
    
//我尝试使用一种更灵活方便的配置来制造命令扩展类
    
//但是又不希望淹没在配置文件和一堆的Class中
    
//既然2种方法都不一定可取,那么就两种一起采用吧,使用哪一种按个人需求来判定
    
//那么这里就使用两种方式来生产命令对象
    
//第一种:
    
//通过一个上一个demo中的第一个类,扩展对象的类. 那样得到最高的访问权限 但是不得不进行一次类型转换 必须进行一次类型判断
    
//第二种
    
//通过 IReceiver 接口 获得更好的封装性 但是得使用反射达到目的,控制能力不是很强
    
//第三种
    
//通过万能的配置文件,获得最灵活的使用方式,但是很显然不得不在反射的基础上,还要进行复杂的配置
    
//
    
//选用哪一种都是无所谓的事情 就看个人爱好了
    
//前两种有区别的就是内部调用方式,但是这里封装的话 还是一样的,我称他为 InlineCmd
    
//后一种就是说的ConfigCmd了
    
//总结下,分析的流程
    
//控制对象<------命令1- - 包装 -  ->命令纸条--- 控制器
    
//    |                                         |
    
//     --<-------命令2- - 包装 -  ->命令纸条--- 
    
//懒得画图 体谅下
    
//通过2种方式对对象进行命令封装 然后 把该命令包装成 命令纸条 然后交给控制器处理
    
//什么啊,比昨天的麻烦多了 不仅要做命令类 还有 命令纸条类
    
//嗯。。没错,但是你编码的时候却没有那么麻烦,纸条包装由我们的命令管理类进行处理了,而且对命令的编写你拥有3种方式自由选择。
    
//控制器那边使用一个清单生成器,进行自动清单生成。 这下可以让人满意了么。
    
//试想一下过程 我们 编写一个控制对象 和 几个命令 剩下的就交给系统处理了。很完美吧!
    
//那么我们来制作吧!
    
//首先我们把命令纸条写写
    
//跳往->CmdMessage.cs

    
//->继续配置
    
//欢迎你又回来了
    
//继续看上面的 纸条完成了 控制器有了? 从那边抓过来一个

    
//跳往万能控制器->

    
//->继续配置之二
    
//就少最后一个东西了包装器
    
//其实包装器有2个东西,一个就是自动包装的类
    
//还有就是包装完成的产品
    
//命令纸条就是包装完成的产品
    
//学会手动包饺子,自动的才知道怎么干的!
    
//这里该使用一个工厂进行配置了
    
//因为将有两种产品
    
//一种是基于类的命令配置
    
//一种是基于属性配置的那么如何生产呢
    
//跳往包装器->


    
//->万能控制器
    /// <summary>
    
/// 万能控制器
    
/// </summary>

    class CmdCilent : ICilent
    
{
        
protected IReceiver _Receiver;

        
public CmdCilent(IReceiver receiver)
        
{
            _Receiver 
= receiver;
        }


        
ICilent 成员
        
//跳往继续配置之二->
    }



    
//->包装器
    
//首先是我们的产品 统一规格的 来看一下
    
//制造一个命令对象
    
//虚的
    abstract class CmdObj : ICmdObj
    
{

        
ICmdObj 成员
    }


    
//首先是类包装产品

    
class ClassCmdObj : CmdObj
    
{
        
/// <summary>
        
/// 这次比较完整一些
        
/// </summary>
        
/// <param name="name">The name.</param>
        
/// <param name="receiver">The receiver.</param>
        
/// <param name="cmdType">Type of the CMD.</param>
        
/// <param name="args">The args.</param>

        public ClassCmdObj(string name, IReceiver receiver, CmdType cmdType, object executeObject, params object[] args)
        
{
            _Name 
= name;
            _Receiver 
= receiver;
            _CmdType 
= cmdType;
            _Args 
= args;
            _ExecuteObject 
= executeObject;
        }

    }


    
/// <summary>
    
/// 工厂接口
    
/// </summary>

    interface ICmdObjFactory
    
{
        ICmdObj Creat(
object t, CmdAttrib arg );
        ICmdObj Creat(ICmdObj tpl,IReceiver receiver);
    }


    
/// <summary>
    
/// 虚工厂
    
/// </summary>

    abstract class CmdObjFactory : ICmdObjFactory
    
{
        
public abstract ICmdObj Creat(object t, CmdAttrib arg);
        
public ICmdObj Creat(ICmdObj tpl, IReceiver receiver)
        
{
            
return new ClassCmdObj(tpl.Name, receiver, tpl.CmdType, tpl.ExecuteObject, tpl.Args);
        }

    }


    
/// <summary>
    
/// 类建造工厂
    
/// </summary>

    class ClassCmdObjFactory : CmdObjFactory
    
{
        
public override ICmdObj Creat(object t, CmdAttrib arg)
        
{
            
string name = t.GetType().Name;
            
switch (name)
            
{
                
case "RuntimeMethodInfo":
                    
return new ClassCmdObj(arg.Cmd, null, CmdType.方法, t, arg.Args);
                    
break;
                
case "RuntimePropertyInfo":
                    
return new ClassCmdObj(arg.Cmd, null, CmdType.属性, t, arg.Args);
                    
break;
            }

           
throw new Exception("不支持的类型!");
            
        }

    }


    
//好了该开始我们的包装器了
    
//跳往CMDManager.cs 包装器->
   //->CmdMessage.cs
    using System.Reflection;





    
/// <summary>
    
/// 命令消息
    
/// </summary>

    [Serializable]
    
internal class CmdMessage:ICmd
    
{
        ICmdObj _msg;
        
/// <summary>
        
/// 开始构造
        
/// 很显然需要一个对命令进行描述的对象
        
/// 当然不能用 object 了 来构造一个
        
/// </summary>
        
/// <param name="?">命令描述</param>

        public CmdMessage(ICmdObj msg)
        
{
            _msg 
= msg;
        }


        
//public string Name
        
//{
        
//    get
        
//    {
        
//        return _msg.Name;
        
//    }
        
//}

        
/// <summary>
        
/// 运行命令
        
/// </summary>

        public void Execute()
        
{
            
switch (_msg.CmdType)
            
{
                
case CmdType.属性:
                    
//无法是设置一个属性
                    
//属性也比较简单的
                    if (_msg.ExecuteObject is PropertyInfo)
                    
{
                        
if (_msg.Args!=null && _msg.Args.Length == 1)
                        
{
                            PropertyInfo finfo 
= _msg.ExecuteObject as PropertyInfo;
                            finfo.SetValue(_msg.Receiver, _msg.Args[
0],null);
                        }

                        
else
                        
{
                            
throw new Exception("必须传入对属性的设置值!");
                        }

                    }

                    
else
                    
{
                        
throw new Exception("命令类型和运行对象不匹配!");
                    }

                    
break;
                
case CmdType.方法:
                    
//调用一个方法
                    
//暂时就以这个为主吧,比较简单
                    
//这里要修改下昨天设计的 ICmd.cs 这个借口了  因为我们使用外部包装 就没必要进行使用字符串名称了 直接使用成员对象了
                    if (_msg.ExecuteObject is MethodInfo)
                    
{
                        MethodInfo minfo 
= _msg.ExecuteObject as MethodInfo;
                        minfo.Invoke(_msg.Receiver, _msg.Args);
                       
                    }

                    
else
                    
{
                        
throw new Exception("命令类型和运行对象不匹配!");
                    }

                    
break;
                
case CmdType.事件:
                    
//事件
                    break;

            }

        }

        
//通用纸条完成!
        
//跳往CmdConfig.cs 继续配置->
    }
license

using System;
using System.Collections.Generic;
using System.Text;

namespace DS_CMD.Command.Demo
{

    
class M邮件群发 : IReceiver
    
{
        
string _S发件数量;

        [CmdAttrib(
"Set100""启动发邮件""100")]
        
public string S发件数量
        
{
            
get return _S发件数量; }
            
set { _S发件数量 = value; }
        }


        
bool _已经启动;

        
public bool 已经启动
        
{
            
get return _已经启动; }
            
set { _已经启动 = value; }
        }


    }



     
class M启动命令 : M邮件群发
    
{

        
/// <summary>
        
/// Initializes a new instance of the <see cref="M启动命令"/> class.
        
/// </summary>
        
/// <param name="_接受者">The _接受者.</param>

         public M启动命令(IReceiver _接受者)
        
{
            _R接受者 
= _接受者 as M邮件群发;
        }



        [CmdAttrib(
"Start""启动发邮件")]
        
public void S启动()
        
{
            _R接受者.已经启动 
= true;
            Console.WriteLine(
"对象已经启动...");
        }


        M邮件群发 _R接受者;


        
/// <summary>
        
/// 运行命令
        
/// </summary>

        public void Execute()
        
{
            S启动();
        }

    }




}


        static void Main(string[] args)
        
{
            
//DS_CMD.Command.Demo._入口._Main一号();
            
//DS_CMD.Command.Demo._入口._Main二号();
            
//DS_CMD.Command.Demo._入口._Main三号();
            DS_CMD.Command.Demo.M邮件群发 s = new DS_CMD.Command.Demo.M邮件群发();
            Command.CMDManager.Instance.CsFileCreat();
            
//运行第一次
            Console.WriteLine("检查状态 >> {0}", s.S发件数量);
            CM邮件群发 c 
= new CM邮件群发(s);
            c.Set100();
            Console.WriteLine(
"检查状态 >> {0}", s.S发件数量);
            Console.WriteLine(
"检查状态 >> {0}", s.已经启动);
            CM启动命令 cs 
= new CM启动命令(s);
            cs.Start();
            Console.WriteLine(
"检查状态 >> {0}", s.已经启动);

            Console.ReadKey();
        }
贴完了,希望大家喜欢 :)
原创粉丝点击