zoukankan      html  css  js  c++  java
  • 【轮子狂魔】轮子的魅力之扩展能力

    序言

    如果你是第一次看本文,建议先看下前面两篇,否则你可能会一头雾水

    看过上一篇【轮子狂魔】打造简易无配置的IoC的人,可能会有几个疑问,我统一回答一下吧。

    1.你这说是IoC,但感觉不像啊。

       首先,我在百度百科里把IoC的概念Copy过来看看。

       控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。

        那么,IoC一定是AutoFac、Unity之类的吗?它什么时候被标签上一定要与这些第三方功能一致的标签?

        在我上篇文中,我使用的是参数注入,缓存反射关系的依赖查找方式。所以我说是简易的IoC应该没什么问题。

    2.有人在上一篇看到了CQRS或者DDD的影子。

        实话说我最近确实看过这方面的东西,受到了一些启发,所以命名上有点贴近这方面技术名词,但这仅仅是名字相近而已。

    3.为什么要出这样一个系列?

        我希望我可以从因果关系上解释出遇到什么样的问题,可以有什么样的方法应对,或者以什么样的顺序来搭建我们的系统架构,这并不是一个标准,而是个人的经验,仅供参考而已。

        另外我也希望大家可以扩展思维,发现架构是可以“造”出来的,而不是“拷贝”出来的,不要落入三层、MVC、MVVM、SOA等里面。

    改造,让架构更智能

    开发微信时发现一个可以提炼出来的共同点:交互接口参数中需携带AccessToken

    而这个AccessToken有多么烦人,在上一篇已经提过我就不再赘述了。

    IAccessTokenAuth的由来

    我们要做的是让架构可以帮助我们自动填充这个AccessToken,那我们就先给他定义为一个接口,方便后面去针对接口操作,

    同时,Event是穿梭在Event、Dispatch、Command三个层中,所以最适合的地方就是在事件里,而接口也是由各个事件自己去实现。

    1     /// <summary>
    2     /// 微信接口交互凭据授权接口
    3     /// </summary>
    4     public interface IAccessTokenAuth
    5     {
    6         string AccessToken { get; set; }
    7     }
    View Code

    DispatchBeforeActiveHandler的由来

    我们要想清楚这个接口需要在什么时候执行?

    就以当前的业务来说,肯定是在执行微信指令之前填充。

    所以执行顺序当然是在普通的调度处理器执行前,为了统一执行方式,我们也需要把这个看作是一个调度处理器,只是执行顺序特殊一些。

    因为调度器建立关系网是根据特性(Attribute)来的,我们就让这个特殊的调度器继承自DispatchHandlerAttribute的类

      

    1     [AttributeUsage(AttributeTargets.Method)]
    2     public class DispatchBeforeActiveHandlerAttribute : DispatchHandlerAttribute
    3     {
    4         public DispatchBeforeActiveHandlerAttribute()
    5             : base(typeof(DispatchHandlerAttribute))
    6         {
    7 
    8         }
    9     }
    View Code
    如何使用IAccessTokenAuth?

    以创建菜单为例

    1     /// <summary>
    2     /// 创建菜单事件
    3     /// </summary>
    4     public class CreateMenuEvent : DispatchEvent, IAccessTokenAuth
    5     {
    6         public string AccessToken { get; set; }
    7 
    8         public MenuList MenuList { get; set; }
    9     }
    View Code
    如何使用DispatchBeforeActiveHandler?

     根据业务分类,我们把FillAccessToken方法加到AccessTokenCommand类中

     1     /// <summary>
     2     /// 微信交互接口凭证命令
     3     /// </summary>
     4     public class AccessTokenCommand
     5     {
     6         #region 静态构造函数
     7 
     8         static AccessTokenCommand()
     9         {
    10             AutoUpdateCache.Add(CacheKeySet.AccessToken.ToString(), new AutoUpdateItem()
    11             {
    12                 UpdateValue = (AutoUpdateItem autoUpdateItem) =>
    13                 {
    14                     var accessTokenInfo = CommandHelper.GetWeChatResponseObject<AccessTokenInfo>(new AccessTokenCommandRequest());
    15 
    16                     autoUpdateItem.ExpiredSeconds = accessTokenInfo.ExpiresIn - 30;//预留过期时效,防止提前过期
    17                     autoUpdateItem.Value = accessTokenInfo;
    18                 }
    19             });
    20         }
    21 
    22         /// <summary>
    23         /// 填充微信接口交互凭据
    24         /// </summary>
    25         /// <param name="e"></param>
    26         [DispatchBeforeActiveHandler()]
    27         public void FillAccessToken(DispatchEvent e)
    28         {
    29             IAccessTokenAuth accessTokenAuth = e as IAccessTokenAuth;
    30             if (accessTokenAuth == null)
    31             {
    32                 return;
    33             }
    34 
    35             var getAccessTokenEvent = new GetAccessTokenEvent();
    36             Dispatcher.ActiveEvent(getAccessTokenEvent);
    37 
    38             accessTokenAuth.AccessToken = getAccessTokenEvent.AccessTokenInfo.AccessToken;
    39         }
    40 
    41         #endregion
    42 
    43         /// <summary>
    44         /// 获取微信交互接口凭证
    45         /// </summary>
    46         /// <param name="e"></param>
    47         [DispatchHandler(typeof(GetAccessTokenEvent))]
    48         public void GetAccessToken(GetAccessTokenEvent e)
    49         {
    50             e.AccessTokenInfo = AutoUpdateCache.GetValue<AccessTokenInfo>(CacheKeySet.AccessToken.ToString());
    51         }
    52     }
    View Code

    我们看到FillAccessToken有一个参数DispatchEvent,一个原因是我们需要为这个事件内的AccessToken赋值,还有一个原因是它是一个基类,这样我们就可以处理任何的事件。

    另外FillAccessToken判断DispatchEvent是不是IAccessTokenAuth接口,来判断是否需要填充AccessToken。

    为什么是做在这里而不是在调度器?因为调度器尽量保持中立,不要接触过于细节的业务逻辑,所以放在这里来校验是否需要填充AccessToken更合适。

    如何让调度器支持DispatchBeforeActiveHandler?

     1.添加一个激活前处理器列表

     2.建立关系网时,分开处理DispatchBeforeActiveHandler和DispatchHandler

     3.在ActiveEvent方法中,执行DispatchHandler之前,先循环遍历DispatchBeforeActiveHandler

      1     /// <summary>
      2     /// 调度器
      3     /// </summary>
      4     public class Dispatcher
      5     {
      6         /// <summary>
      7         /// 调度关系网
      8         /// </summary>
      9         private static Dictionary<Type, List<DispatchHandlerAttribute>> _dicDispatchRelativeNetwork = new Dictionary<Type, List<DispatchHandlerAttribute>>();
     10 
     11         /// <summary>
     12         /// 激活前处理器列表
     13         /// </summary>
     14         private static List<DispatchHandlerAttribute> _lstBeforeActiveHandler = new List<DispatchHandlerAttribute>();
     15 
     16         /// <summary>
     17         /// 建立调度关系网
     18         /// </summary>
     19         /// <param name="assembly"></param>
     20         public static void BuildDispatchRelationship(Assembly assembly)
     21         {
     22             Logger.Info("调度器:开始建立调度关系网...");
     23 
     24             var types = assembly.GetTypes();
     25 
     26             foreach (var type in types)
     27             {
     28                 var methods = type.GetMethods();
     29 
     30                 foreach (var method in methods)
     31                 {
     32                     var attribute = method.GetCustomAttributes(typeof(DispatchHandlerAttribute), true).FirstOrDefault();
     33                     if (attribute != null)
     34                     {
     35                         CheckParameterRule(method);
     36 
     37                         var handler = attribute as DispatchHandlerAttribute;
     38                         handler.DispatchInstance = Activator.CreateInstance(type);
     39                         handler.ActionMethodInfo = method;
     40 
     41                         if (attribute is DispatchBeforeActiveHandlerAttribute)
     42                         {
     43                             AddBeforeActiveHandler(handler);
     44                         }
     45                         else
     46                         {
     47                             AddDispatchRelation(handler);
     48                         }
     49                     }
     50                 }
     51             }
     52         }
     53 
     54         /// <summary>
     55         /// 添加激活前处理器
     56         /// </summary>
     57         /// <param name="handler">调度处理器</param>
     58         private static void AddBeforeActiveHandler(DispatchHandlerAttribute handler)
     59         {
     60             _lstBeforeActiveHandler.Add(handler);
     61 
     62             Logger.Info(string.Format("调度器:增加激活前处理器 [{0}]-[{1}.{2}]", handler.ObservedDispatchEventType.Name, handler.DispatchInstance.GetType().Name, handler.ActionMethodInfo.Name));
     63         }
     64 
     65         /// <summary>
     66         /// 添加调度关系
     67         /// </summary>
     68         /// <param name="handler">调度处理器</param>
     69         private static void AddDispatchRelation(DispatchHandlerAttribute handler)
     70         {
     71             var eventType = handler.ObservedDispatchEventType;
     72 
     73             if (!_dicDispatchRelativeNetwork.ContainsKey(eventType))
     74             {
     75                 _dicDispatchRelativeNetwork.Add(eventType, new List<DispatchHandlerAttribute>());
     76             }
     77 
     78             _dicDispatchRelativeNetwork[eventType].Add(handler);
     79 
     80             Logger.Info(string.Format("调度器:建立新的关系网 [{0}]-[{1}.{2}]", eventType.Name, handler.DispatchInstance.GetType().Name, handler.ActionMethodInfo.Name));
     81         }
     82 
     83         /// <summary>
     84         /// 检查参数规则
     85         /// </summary>
     86         /// <param name="method"></param>
     87         private static void CheckParameterRule(MethodInfo method)
     88         {
     89             var parameters = method.GetParameters();
     90             if (parameters == null ||
     91                 parameters.Length != 1 ||
     92                 (!parameters.FirstOrDefault().ParameterType.Equals(typeof(DispatchEvent)) &&
     93                  !parameters.FirstOrDefault().ParameterType.BaseType.Equals(typeof(DispatchEvent))
     94                 ))
     95             {
     96                 throw new Exception(string.Format("DispatchHandler - [{0}]的参数必须是只有一个且继承自DispatchEvent", method.Name));
     97             }
     98         }
     99 
    100         /// <summary>
    101         /// 激活事件
    102         /// </summary>
    103         /// <param name="dispatchEvent">调度事件</param>
    104         public static void ActiveEvent(DispatchEvent dispatchEvent)
    105         {
    106             var type = dispatchEvent.GetType();
    107 
    108             if (!_dicDispatchRelativeNetwork.ContainsKey(type))
    109             {
    110                 Logger.Error(string.Format("调度器:当前事件[{0}]没有找到绑定的Handler", type.FullName));
    111                 return;
    112             }
    113 
    114             _lstBeforeActiveHandler.ForEach(action =>
    115             {
    116                 ActiveAction(action, dispatchEvent);
    117             });
    118 
    119             _dicDispatchRelativeNetwork[type].ForEach(action =>
    120             {
    121                 ActiveAction(action, dispatchEvent);
    122             });
    123         }
    124 
    125         private static void ActiveAction(DispatchHandlerAttribute action, DispatchEvent dispatchEvent)
    126         {
    127             try
    128             {
    129                 action.ActionMethodInfo.Invoke(action.DispatchInstance, new object[] { dispatchEvent });
    130             }
    131             catch (Exception ex)
    132             {
    133                 if (ex.InnerException != null && ex.InnerException.GetType().Equals(typeof(WCFLib.ExceptionExtension.GException)))
    134                 {
    135                     throw ex.InnerException;
    136                 }
    137                 else
    138                 {
    139                     throw;
    140                 }
    141             }
    142         }
    143     }
    View Code
    激活Event的代码变成什么样子?
    1             var getAccessTokenEvent = new GetAccessTokenEvent();
    2             Dispatcher.ActiveEvent(getAccessTokenEvent);
    3             var accessTokenInfo = getAccessTokenEvent.AccessTokenInfo; 


    是的,你没看错,没有变化!

    同时,在真正的创建菜单事件中也不需要处理任何有关AccessToken的业务逻辑。

    因为创建菜单的指令也很简单,我就不贴代码了,跟GetAccessToken差不多,只是处理微信指令时逻辑上不太一样而已。

    为什么我喜欢造轮子?

     在满足技术条件的基础上,轮子的可控性更高,可扩展性也更高。

     轮子的发展方向会根据我们的业务进行调整,同时去掉了我们并不需要的一些附带功能。

     绝大多数第三方是做一个通用的类库,而我造的轮子不是,我造的是更贴近系统业务的,更贴近团队技术水平的。

     不论是从封装也好,调用也好,各个角度来看,我们都能够控制轮子的形态,让轮子成为我们非常熟悉的调用方式。学习成本会也自然就会降低。

     轮子有好有坏,看你从哪个方面来看,这个不需要去争论,我们只需要保持着可用性、可扩展性、性能达标的标准去走即可。

     最后,我并不鼓励盲目的造轮子,这是我从始至终的观点,但我仍然坚持造最适合自己的轮子,两者并不冲突。 ^_^

  • 相关阅读:
    ARC 117 D
    Maven依赖踩坑记录
    MobaXterm连接本地CentOS7
    Git在IDEA下的常用操作
    mq消息中间件
    nginx的作用
    Git的使用
    docker
    redis
    导出excel
  • 原文地址:https://www.cnblogs.com/doddgu/p/lunzikuangmo_lunzidemeili.html
Copyright © 2011-2022 走看看