控制注入原则,DIP,Dependency Inversion Principle
- 高层模块不应该依赖于低层模块。而两者都应该依赖于抽象(接口)。
- 抽象不应该依赖于细节,细节应该依赖于抽象。
DIP通过让高层模块依赖于抽象而不是依赖于低层模块的具体的实现,帮助我们开发松耦合的代码。反转控制模式是这个原则的一个实现方式。
反转控制,IoC,Inversion of Control
IoC指的是一种编程风格,在框架或runtime上控制程序的流程。IoC改变了正常的控制方式,它运行于DIP之上,大多数.NET Framework的软件都用IoC。
比如:
- 当写ASP.NET的程序时,进入了ASP.NET页面的生命周期,但是不能控制它。
- 当写WCF服务时,用属性装饰实现的接口。你可能写服务代码,但是从根本上没有控制WCF。
更多的,IoC是一个通用的术语,而不局限于DI。实际上,DI和Service Locator模式是特定的IoC模式的版本,或者可以说DI和Service Locator是实现IoC的方式。
比如,假设Client类需要使用Service类组件,然后能做的事情是让Client类知道IService接口而不是Service类。用这种方式,可以在任何时候改变Service类的实现,而不用破坏host的代码。
IoC和DIP
DIP说高层模块不应该依赖于低层模块,而两者都应该依赖于抽象。IoC是一种提供抽象的方式,是一种改变控制的方式。IoC给出了一些实现DIP的方式。如果你想让高层模块和低层模块相对独立,那么需要反转控制,这样低层模块不控制接口和对象的创建,最后IoC给出一些控制反转的方式。
IoC和DI
术语依赖注入DI(Dependency Injection)和控制反转IoC(Inversion of Control)通常可以互换,来描述相同的设计模式。这种模式起初叫IoC,但是Martin Fowler提出了DI,因为所有的框架都在某种程度上反转控制,然后他想更具体地指定哪方面的控制被反转了。
依赖注入,DI,Dependency Injection
DI是一个软件设计模式,允许我们开发松耦合的代码。DI是一种很好的方式来减少软件组件间的紧耦合。DI还允许我们更好的管理未来的改变和软件中的其他复杂性。DI的目的是让代码可维护。
依赖注入模式使用一个builder对象来初始化,并提供对象所需的依赖,意味着它允许你从类外“注入”依赖。
SL,Service Locator
Service Locator是一个软件设计模式,允许我们开发松耦合的代码。它实现了DIP原则,并且可以容易地用在现有的代码上,因为它使得整体的设计变得宽松,而不用改变public的接口。
Service Locator模式引入了一个locator对象,用来解决依赖,意味着它允许在类中“解决”依赖。
DI和SL
我们来考虑两个类之间的简单地依赖,如下图:
public class Service
{
public void Serve()
{
Console.WriteLine("Service Called");
//To Do: Some Stuff
}
}
public class Client
{
private Service _service;
public Client()
{
this._service = new Service();
}
public void Start()
{
Console.WriteLine("Service Started");
this._service.Serve();
//To Do: Some Stuff
}
}
明显,Client类依赖于Service类。如果你想让它松耦合,你不得不使用IoC让它更灵活和能够重用。
要实现IoC,可以用SL和DI。SL允许你在一个类中“解决”依赖,DI允许你从类外“注入”依赖。
使用Service Locator
public interface IService
{
void Serve();
}
public class Service : IService
{
public void Serve()
{
Console.WriteLine("Service Called");
//To Do: Some Stuff
}
}
public static class LocateService
{
public static IService _Service { get; set; }
public static IService GetService()
{
if (_Service == null)
_Service = new Service();
return _Service;
}
}
public class Client
{
private IService _service;
public Client()
{
this._service = LocateService.GetService();
}
public void Start()
{
Console.WriteLine("Service Started");
this._service.Serve();
//To Do: Some Stuff
}
}
Service Locator的实现:
class Program
{
static void Main(string[] args)
{
var client = new Client();
client.Start();
Console.ReadKey();
}
}
反转发生在构造函数中,通过放置Service实现IService接口。依赖通过Locator组装起来。
使用依赖注入
public interface IService
{
void Serve();
}
public class Service : IService
{
public void Serve()
{
Console.WriteLine("Service Called");
//To Do: Some Stuff
}
}
public class Client
{
private IService _service;
public Client(IService service)
{
this._service = service;
}
public void Start()
{
Console.WriteLine("Service Started");
this._service.Serve();
//To Do: Some Stuff
}
}
使用Builder的实现:
class Program
{
static void Main(string[] args)
{
client = new Client(new Service());
client.Start();
Console.ReadKey();
}
}
依赖注入发生在构造函数中,通过放置Service实现IService接口。依赖通过Builder组装起来,Builder负责:
- 知道每个IService的类型。
- 根据请求,把抽象的IService喂给Client。
依赖注入 VS Service Locator
- 当使用service locator时,每个类都有一个对service locator的依赖。而依赖注入只在启动时调用一次,把依赖注入到main类中。
- service locator模式易用于现有的代码中,不用修改public的接口就可以松耦合。但是可读性比依赖入要差。