程序中经常用到IoC(控制反转)和DI(依赖注入),但对其概念的认识也是模模糊糊,今天就详细的了解下
- 什么是IOC(Inversion of Control )
- 什么是DI(Dependency Injection )
- 实现依赖注入的方式
- IOC、DI的优点
IOC控制反转
回想下我们在大学里,有时候需要去参加一些组织活动或者是无聊的讲座。就把College当成类,活动或讲座看成Event。让我们试着把College和Event与IOC联系起来
假设我们有两个类,College和TechEvents,如上图代码,你会发现一些问题:
- 这两个类彼此紧密耦合。我们不能有一个没有TecheEvents的College,因为TecheEvents对象是在College构造函数中创建的
- 如果我修改了TechEvents,我也需要编译或修改College类
- College控制Event的产生。College现在是一个单一事件(社团活动)的组织,如果有其他事件(如FootballEvent、PartyEvent)发生,那就需要修改College,因为College是直接引用的Events
现在我们需要解决这个问题,否则在College里,不能有其他的Event
解决这个问题的办法是把事件组织的控制转移到其他地方,就是我们常说的IOC(控制反转)。把控制权反转给其他实体,而不是直接在College中。
IOC的原理是什么呢?
Don't Call Me, we will call you
换言之,Main类不应该有聚合类的具体实现,而应该依赖于该类的抽象。College类应该依赖于TechEvents类的抽象(接口或者抽象类)
//1.创建用来实现抽象的接口interface IEvent { void LoadEventDetail(); }//2.所有的Event类应实现IEvent接口class TechEvents : IEvent{public void LoadEventDetail(){Console.WriteLine("Technology Event Details");}}class FootballEvent:IEvent{public void LoadEventDetail(){Console.WriteLine("Football Event Details");}}class PartyEvent:IEvent{public void LoadEventDetail(){Console.WriteLine("Party Event Details");}}//3.College构造函数说:I will call the Eventclass College{private IEvent _events = null;public College(IEvent ie){this._events = ie;}public void GetEvents(){this._events.LoadEventDetail();}}
DI依赖注入
IOC通过DI完成。它解释如何将具体实现注入到使用了抽象类或接口的类中。依赖注入的主要思想是减少类之间的耦合,并将抽象和具体实现的绑定从依赖类中移除。简单来说,DI是一个对象如何知道被抽象的其他对象。
实现依赖注入的方法主要有4种。
1. 构造器注入
上面已经讨论了这种方法,具体类的对象被传递给依赖类的构造函数。
class College{private IEvent _events = null;public College(IEvent ie){this._events = ie;}public void GetEvents(){this._events.LoadEventDetail();}}
事件对象由构造器注入,使其松散耦合。College类将完成它的工作,如果它想获取事件的相关细节,它将基于他想调用的事件,在构造函数中调用它。
等同于
College coll = new College(new FootballEvent());
除了这个优点,另一个优点是当Event发生改变或增加事件,College不需要关心这些。
2. 属性注入
这是最常用的方法,我们通过创建一个类型为接口的属性来注入具体的类
class College{private IEvent _events;public IEvent MyEvent{set{_events = value;}}
//....}
MyEvent属性的setter将具体对象绑定到接口。类与具体对象松散耦合。现在,Event类任何对象的变化都不会影响到College类。
College coll = new College();
coll.MyEvent = new FootballEvent();
3. 方法注入
这个方法中,具体的类对象通过方法参数传递给依赖类。
class College
{ private IEvent _events; public void GetEvent(IEvent myevent) { this._events = myevent; }
//....
}
如上所示,通过GetEvent方法调用College事件,其中事件的类型作为抽象类型的参数传递。
College col1 = new College();col1.GetEvent(new FootballEvent());col1.GetEvents();
4. 服务定位注入
service locator就像一个简单的运行时mapper。允许在运行时添加代码,而不必重新编译应用程序,并且在某些情况下不必重新启动。
class College{private IEvent _events = null;EventLocator el = new EventLocator();public College(int index){this._events = el.LocateEvent(index);}public void GetEvents(){this._events.LoadEventDetail();}}class EventLocator{public IEvent LocateEvent(int index){if (index == 1) return new FootballEvent();else if (index == 2) return new PartyEvent();else return new TechEvents();}}
上面的代码中,在Events和College类之间有一个EventLocator类,它帮助我在不知道具体类型的情况下定位服务。通过构造函数中的index值,来调用第三方进行定位,并返回给构造函数。EventLocator类的任何改变都不会影响到College类。
College coll = new College(1);
coll.GetEvents();
实施这一原则的优点
- 它有助于类的解耦。
- 由于解耦,增加了代码的可重用性。
- 提升了代码的可维护性和测试。
总结:控制反转(IOC)讨论谁将发起调用,而依赖注入(DI)讨论一个对象如何通过抽象获得对其他对象的依赖。
参考地址:https://www.c-sharpcorner.com/UploadFile/cda5ba/dependency-injection-di-and-inversion-of-control-ioc/