为更好理解依赖注入模式,特意去了解服务定位器模式,今日留下一文笔记,以助巩固。
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方法想要的结果,难道我还要加个修改已经注册了服务的方法,那我修改某个注册的服务,就不会别的处理对象实例产生影响吗?岂不糟糕透了!
窟窿有了就要补,我们是高级动物,得学会思考解决问题。
如果不使用静态了,类不用静态修饰,成员不用静态修饰。把服务定位器成为一个注入点(依赖注入),结果会怎样呢?
我的理解,若追根溯源,详看大牛讲解文章
下篇服务定位器之依赖注入模式,继续分享~
☆☆☆ 如有错漏之处,希望大家不吝指出。如果喜欢,希望大家多多点赞。笔芯!☆☆☆