处理依赖的模式
服务器定位模式
void Copy()
{
Byte byte;
var reader = ServiceLocator.GetService<IReader>();
var writer = ServiceLocator.GetService<IWriter>();
while (byte == reader.Read())
writer.Write(byte);
}
使用ServiceLocator返回特定实例,它充当一个工厂。通常的做法是把ServiceLocator创建成一个类,暴露少数静态工厂方法。
public class ServiceLocator
{
public Object GetService(Type typeToResolve) { ... }
public T GetService<T>() { ... }
public Object GetService(string typeNickName) { ... }
}
可以根据参数获取想要的类型
public object GetService(string typeNickName)
{
if (typeNickName == "sometype")
return new SomeType();
if (typeNickName == "someothertype")
return new SomeOtherType();
}
显然,服务定位器可以采用很复杂的实例化方式(间接创建,对象池,单例),从某个配置文件读取抽象类型和具体类型的映射。
适用场景
最适合它的场景:在一个很难通过其他方式重新设计的某个大型遗留代码,并且需要添加扩展时。
缺点
这个模式的缺点是需要你深入代码才能弄清如何处理依赖。
重要:在多数情况下,服务定位器被看作反模式。理由是你的代码最终会遍布服务定位器类的引用。更糟糕的情况是,直到运行时你才会发现错误。
依赖注入模式
也控制反转或者IOC
void Copy(IReader reader,IWriter writer )
{
Byte byte;
while (byte == reader.Read())
writer.Write(byte);
}
注入依赖有3种方式:使用构造函数、写入属性或者接口。
一般通过构造函数注入,因为这样可以从一开始就清楚表明一个类有什么依赖。基于构造函数的依赖注入也是检测一个类是否“臭”了的绝佳方式。如果发现一个类的构造函数里有20个依赖,很可能这个类没有遵循SRP!
依赖注入的关键是位于这个类之外的工厂代码。但是,这个代码在一些重要的场景里可能是重复的、冗长的。它可能很沉闷,容易出错,极大地抵消了这个模式的好处。有鉴于此,依赖注入通常需要特定的生产力工具,也称为IOC容器。
IOC容器是围绕一个通过某些配置信息解析依赖的容器对象构建的。调用方代码实例化这个容器,并以参数的形式传递想要的接口。作为响应,IOC框架返回一个实现那个接口的具体对象。正确实施依赖注入可能只需直接调用一次IOC容器,所有这些只要几行代码,因为大多数依赖通常会被牵涉的类的构造函数处理。
Microsoft目前提供几个IOC选择。一个是MEF,它主要解决扩展性问题,在客户端应用程序里提供绝佳的插件支持。此外,有MEF2和Unity(unity.codeplex.com)。
Unity和MEF2都可以看作完整的IOC容器。另一方面,MEF只提供IOC容器的基本功能。