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三个层中,所以最适合的地方就是在事件里,而接口也是由各个事件自己去实现。

    /// <summary>
    /// 微信接口交互凭据授权接口
    /// </summary>
    public interface IAccessTokenAuth
    {
    string AccessToken { get; set; }
    }

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

    DispatchBeforeActiveHandler的由来

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

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

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

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

      

    1     [AttributeUsage(AttributeTargets.Method)]
    2     public class DispatchBeforeActiveHandlerAttribute : DispatchHandlerAttribute
    3     {
    4         public DispatchBeforeActiveHandlerAttribute()
    5             : base(typeof(DispatchHandlerAttribute))
    6         {
    7 
    8         }
    9     }
    复制代码
    1     [AttributeUsage(AttributeTargets.Method)]
    2     public class DispatchBeforeActiveHandlerAttribute : DispatchHandlerAttribute
    3     {
    4         public DispatchBeforeActiveHandlerAttribute()
    5             : base(typeof(DispatchHandlerAttribute))
    6         {
    7 
    8         }
    9     }
    复制代码
    如何使用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     }
    如何使用DispatchBeforeActiveHandler?

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

     

    /// <summary>
    /// 微信交互接口凭证命令
    /// </summary>
    public class AccessTokenCommand
    {
    #region 静态构造函数

    static AccessTokenCommand()
    {
    AutoUpdateCache.Add(CacheKeySet.AccessToken.ToString(), new AutoUpdateItem()
    {
    UpdateValue = (AutoUpdateItem autoUpdateItem) =>
    {
    var accessTokenInfo = CommandHelper.GetWeChatResponseObject<AccessTokenInfo>(new AccessTokenCommandRequest());

    autoUpdateItem.ExpiredSeconds = accessTokenInfo.ExpiresIn - 30;//预留过期时效,防止提前过期
    autoUpdateItem.Value = accessTokenInfo;
    }
    });
    }

    /// <summary>
    /// 填充微信接口交互凭据
    /// </summary>
    /// <param name="e"></param>
    [DispatchBeforeActiveHandler()]
    public void FillAccessToken(DispatchEvent e)
    {
    IAccessTokenAuth accessTokenAuth = e as IAccessTokenAuth;
    if (accessTokenAuth == null)
    {
    return;
    }

    var getAccessTokenEvent = new GetAccessTokenEvent();
    Dispatcher.ActiveEvent(getAccessTokenEvent);

    accessTokenAuth.AccessToken = getAccessTokenEvent.AccessTokenInfo.AccessToken;
    }

    #endregion

    /// <summary>
    /// 获取微信交互接口凭证
    /// </summary>
    /// <param name="e"></param>
    [DispatchHandler(typeof(GetAccessTokenEvent))]
    public void GetAccessToken(GetAccessTokenEvent e)
    {
    e.AccessTokenInfo = AutoUpdateCache.GetValue<AccessTokenInfo>(CacheKeySet.AccessToken.ToString());
    }
    }

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

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

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

    如何让调度器支持DispatchBeforeActiveHandler?

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

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

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

     

    /// <summary>
    /// 调度器
    /// </summary>
    public class Dispatcher
    {
    /// <summary>
    /// 调度关系网
    /// </summary>
    private static Dictionary<Type, List<DispatchHandlerAttribute>> _dicDispatchRelativeNetwork = new Dictionary<Type, List<DispatchHandlerAttribute>>();

    /// <summary>
    /// 激活前处理器列表
    /// </summary>
    private static List<DispatchHandlerAttribute> _lstBeforeActiveHandler = new List<DispatchHandlerAttribute>();

    /// <summary>
    /// 建立调度关系网
    /// </summary>
    /// <param name="assembly"></param>
    public static void BuildDispatchRelationship(Assembly assembly)
    {
    Logger.Info("调度器:开始建立调度关系网...");

    var types = assembly.GetTypes();

    foreach (var type in types)
    {
    var methods = type.GetMethods();

    foreach (var method in methods)
    {
    var attribute = method.GetCustomAttributes(typeof(DispatchHandlerAttribute), true).FirstOrDefault();
    if (attribute != null)
    {
    CheckParameterRule(method);

    var handler = attribute as DispatchHandlerAttribute;
    handler.DispatchInstance = Activator.CreateInstance(type);
    handler.ActionMethodInfo = method;

    if (attribute is DispatchBeforeActiveHandlerAttribute)
    {
    AddBeforeActiveHandler(handler);
    }
    else
    {
    AddDispatchRelation(handler);
    }
    }
    }
    }
    }

    /// <summary>
    /// 添加激活前处理器
    /// </summary>
    /// <param name="handler">调度处理器</param>
    private static void AddBeforeActiveHandler(DispatchHandlerAttribute handler)
    {
    _lstBeforeActiveHandler.Add(handler);

    Logger.Info(string.Format("调度器:增加激活前处理器 [{0}]-[{1}.{2}]", handler.ObservedDispatchEventType.Name, handler.DispatchInstance.GetType().Name, handler.ActionMethodInfo.Name));
    }

    /// <summary>
    /// 添加调度关系
    /// </summary>
    /// <param name="handler">调度处理器</param>
    private static void AddDispatchRelation(DispatchHandlerAttribute handler)
    {
    var eventType = handler.ObservedDispatchEventType;

    if (!_dicDispatchRelativeNetwork.ContainsKey(eventType))
    {
    _dicDispatchRelativeNetwork.Add(eventType, new List<DispatchHandlerAttribute>());
    }

    _dicDispatchRelativeNetwork[eventType].Add(handler);

    Logger.Info(string.Format("调度器:建立新的关系网 [{0}]-[{1}.{2}]", eventType.Name, handler.DispatchInstance.GetType().Name, handler.ActionMethodInfo.Name));
    }

    /// <summary>
    /// 检查参数规则
    /// </summary>
    /// <param name="method"></param>
    private static void CheckParameterRule(MethodInfo method)
    {
    var parameters = method.GetParameters();
    if (parameters == null ||
    parameters.Length != 1 ||
    (!parameters.FirstOrDefault().ParameterType.Equals(typeof(DispatchEvent)) &&
    !parameters.FirstOrDefault().ParameterType.BaseType.Equals(typeof(DispatchEvent))
    ))
    {
    throw new Exception(string.Format("DispatchHandler - [{0}]的参数必须是只有一个且继承自DispatchEvent", method.Name));
    }
    }

    /// <summary>
    /// 激活事件
    /// </summary>
    /// <param name="dispatchEvent">调度事件</param>
    public static void ActiveEvent(DispatchEvent dispatchEvent)
    {
    var type = dispatchEvent.GetType();

    if (!_dicDispatchRelativeNetwork.ContainsKey(type))
    {
    Logger.Error(string.Format("调度器:当前事件[{0}]没有找到绑定的Handler", type.FullName));
    return;
    }

    _lstBeforeActiveHandler.ForEach(action =>
    {
    ActiveAction(action, dispatchEvent);
    });

    _dicDispatchRelativeNetwork[type].ForEach(action =>
    {
    ActiveAction(action, dispatchEvent);
    });
    }

    private static void ActiveAction(DispatchHandlerAttribute action, DispatchEvent dispatchEvent)
    {
    try
    {
    action.ActionMethodInfo.Invoke(action.DispatchInstance, new object[] { dispatchEvent });
    }
    catch (Exception ex)
    {
    if (ex.InnerException != null && ex.InnerException.GetType().Equals(typeof(WCFLib.ExceptionExtension.GException)))
    {
    throw ex.InnerException;
    }
    else
    {
    throw;
    }
    }
    }
    }

    激活Event的代码变成什么样子?
    1             var getAccessTokenEvent = new GetAccessTokenEvent();
    2             Dispatcher.ActiveEvent(getAccessTokenEvent);
    3             var accessTokenInfo = getAccessTokenEvent.AccessTokenInfo; 


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

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

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

    为什么我喜欢造轮子?

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

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

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

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

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

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

  • 相关阅读:
    图论4-floyd
    。。。
    [LOJ10164]数字游戏
    KMP模板
    无向图割点模板
    tarjan有向图模板
    LCA倍增模板
    P2149 [SDOI2009]Elaxia的路线
    树的直径dp模板
    [暑假集训]Day4 T3 平板涂色
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/4179037.html
Copyright © 2011-2022 走看看