服务定位器之反模式

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

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方法想要的结果,难道我还要加个修改已经注册了服务的方法,那我修改某个注册的服务,就不会别的处理对象实例产生影响吗?岂不糟糕透了!

窟窿有了就要补,我们是高级动物,得学会思考解决问题。

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

我的理解,若追根溯源,详看大牛讲解文章
下篇服务定位器之依赖注入模式,继续分享~
☆☆☆ 如有错漏之处,希望大家不吝指出。如果喜欢,希望大家多多点赞。笔芯!☆☆☆

猜你喜欢

转载自www.cnblogs.com/bibi-feiniaoyuan/p/9184350.html
今日推荐