• 服务定位器之反模式


    为更好理解依赖注入模式,特意去了解服务定位器模式,今日留下一文笔记,以助巩固。

    Martin Fowler提出的服务器定位器概念实际上是一种反模式,简单而言,服务器隐藏了类之间依赖关系,从而增加了维护难度。

    服务定位器实现代码如下:

    public static class Locator
    {
        //定位器服务集合
        private readonly static Dictionary<Type, Func<object>>
            services = new Dictionary<Type, Func<object>>();
    
         //往定位器的服务塞东西  -注册服务
        public static void Register<T>(Func<T> resolver)   
        {
            Locator.services[typeof(T)] = () => resolver();
        }
        
        //根据参数从定位器的服务里拿东西  -解析 
        public static T Resolve<T>()       
        {
            return (T)Locator.services[typeof(T)]();
        }
        
        //清空定位器的服务 
        public static void Reset()
        {
            Locator.services.Clear();
        }
    }
    真正的服务定位器代码实现比这个要复杂,但是这个实现已经抓住了重点。(不要过于纠结于此,先理解是关键)。

    现在创建订单处理对象,对象里使用上面的静态的服务定位器,代码如下:

    public class OrderProcessor : IOrderProcessor
    {
        public void Process(Order order)
        {
            //从定位器里拿出注册该接口的服务   (定位器不一定有注册了该接口的服务,运行时可能抛异常,编译时是不能知道有没有这个服务的。
            var validator = Locator.Resolve<IOrderValidator>();  
            if (validator.Validate(order))
            {
                //从定位器里拿出注册该接口的服务   (定位器不一定有注册了该接口的服务,运行时可能抛异常,编译时是不能知道有没有这个服务的。
                var shipper = Locator.Resolve<IOrderShipper>();
                shipper.Ship(order);
            }
        }
    }

    现在假设我们是上面订单的消费者,如何处理订单的接口都外包给第三方做了,我们并不清楚处理订单的源码。我们创建订单(通过IDE的代码提示我们清楚有这么个订单对象),代码如下:

    var dealOrder = new OrderProcessor();

    创建订单,调用处理订单方法,代码如下:

    var order = new Order();
    var dealOrder   = new OrderProcessor();
    sut.Process(order);

    运行这段代码(即运行时)抛出了一个KeyNotFoundException(主键找不到异常),因为IOrderValidator从来没有在定位器上注册过,通过仔细阅读源代码或查阅文档,又或问第三方技术支持者,我们可能最终会发现,在运行代码之前,我们需要用服务定位器给IOrderValidator接口注册一个服务,该服务是一个静态类。

    烦躁,这种开发体验并不是那么友好!之前都没有告诉要注册这个服务,等我执行后才告诉我去补工作。气呀!

    更令人郁闷的是,如果我运行成功了,不抛异常,我所调用的服务会不会在别的地方(我不知道的地方,毕竟大部分公司都是团队开发项目)注册的,而这个服务又不是我真的想要的。

    上面的服务定位器里多处用到static关键字:静态类、静态成员,这个东西不能随便用。 static关键字学习

    服务定位器使用静态类会产生如下弊端
    1、因为类是静态类,所以到处的订单处理对象实例都是共用一个服务定位器对象。到处都可以注册服务,多危险呀。当前对象会不会一不小心用到了不该用的服务呢?

    2、如果想扩展服务定位器的成员方法,例如代码如下:

    public void Process(Order order)
    {
        var validator = Locator.Resolve<IOrderValidator>();
        if (validator.Validate(order))
        {
            //多用了个服务
            var collector = Locator.Resolve<IOrderCollector>();  
            collector.Collect(order);
            var shipper = Locator.Resolve<IOrderShipper>();
            shipper.Ship(order);
        }
    }

    会不会在扩展'增加使用多一个IOrderCollector的服务'之前,就有别处给这个定位器注册这个服务,而这个服务又不是某个订单处理调用Process方法想要的结果,难道我还要加个修改已经注册了服务的方法,那我修改某个注册的服务,就不会别的处理对象实例产生影响吗?岂不糟糕透了!

    如果不使用静态了,类不用静态修饰,成员不用静态修饰。把服务定位器成为一个注入点(依赖注入),结果会怎样呢?

    参考文章: http://blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern

  • 相关阅读:
    24、合并两个有序链表
    23、反转链表
    22、删除链表的倒数第N个节点
    21、删除链表中的节点
    18、实现strStr()
    17、字符串转换整数 (atoi)
    15、有效的字母异位词
    16、验证回文字符串
    14、字符串中的第一个唯一字符
    mybatis入门(七)----延迟加载
  • 原文地址:https://www.cnblogs.com/bibi-feiniaoyuan/p/9184350.html
Copyright © 2020-2023  润新知