控制反转IOC的依赖注入方式
引言:
项目中遇到关于IOC的一些内容,因为和正常的逻辑代码比较起来,IOC有点反常。因此本文记录IOC的一些基础知识,并附有相应的简单实例,而在实际项目中再复杂的应用也只是在基本应用的基础上扩展而来的。本文目的两个,一是记录学习过程,以便将来温故;二是请大牛对小弟指点一二。
概念:
控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。依赖注入应用比较广泛。-百度百科。
简单实例:
下面编写使用IOC实现的简单实例,以便大家对IOC有个初始的概念。功能是输入字符串格式的日志,代码如下:
接口ILogger和具体的Logger类:
public interface ILogger { void Log(string msg); } public class Logger : ILogger { public virtual void Log(string msg) { Console.WriteLine("msg is {0}", msg); } }
常规的调用方式:
ILogger logger = new Logger(); logger.Log("good");
使用IOC模式的客户端调取方式:
添加using Microsoft.Practices.Unity;
IUnityContainer container = new UnityContainer(); container.RegisterType<ILogger, Logger>(); ILogger logger = container.Resolve<ILogger>(); logger.Log("good");
结果如下:
介绍常用的概念:依赖、依赖倒置、控制反转、依赖注入。
日常编码过程中,新建一个类,进而new对象进而进行相关的业务逻辑,例如下面实例,用户播放媒体文件:
public class OperationMain { public void PlayMedia() { MediaFile _mtype = new MediaFile(); Player _player = new Player(); _player.Play(_mtype); } } public class Player { public void Play(MediaFile file) { Console.WriteLine(file.FilePath); } } public class MediaFile { public string FilePath { get; set; } }
从上文可以看出代码的耦合度太高了,如果有新的需求,需要改动的地方太多了,那么使用依赖倒置原则来降低耦合度。
依赖倒置原则:
高层模块不应该依赖于低层模块,两者应该依赖于抽象;
抽象不应该依赖于具体,具体应该依赖于抽象;
public class OperationMain { public void PlayMedia() { IMediaFile _mtype = new MediaFile(); IPlayer _player = new Player(); _player.Play(_mtype); } } public interface IPlayer { void Play(IMediaFile file); } public class Player : IPlayer { public void Play(IMediaFile file) { Console.WriteLine(file.FilePath); } } public interface IMediaFile { string FilePath { get; set; } } public class MediaFile : IMediaFile { public string FilePath { get; set; } }
控制反转:
控制反转(IoC),它为相互依赖的组件提供抽象,将依赖(低层模块)对象的获得交给第三方(系统)来控制,即依赖对象不在被依赖模块的类中直接通过new来获取。
控制反转IoC是Inversion of Control的缩写,是说对象的控制权进行转移,转移到第三方,比如转移交给了IoC容器,它就是一个创建工厂,你要什么对象,它就给你什么对象,有了IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建立它们之间的关系。
依赖注入:
依赖注入,控制反转(IoC)一种重要的方式,就是将依赖对象的创建和绑定转移到被依赖对象类的外部来实现。
依赖注入,就是由IoC容器在运行期间,动态地将某种依赖关系注入到对象之中。
public class OperationMain { IMediaFile _mtype; IPlayer _player; public OperationMain(IPlayer player, IMediaFile mtype) { _player = player; _mtype = mtype; } public void PlayMedia() { _player.Play(_mtype); } }
调用方式:
static UnityContainer container = new UnityContainer(); static void init() { container.RegisterType<IPlayer, Player>(); container.RegisterType<IMediaFile, MediaFile>(); } static void Main(string[] args) { init(); OperationMain op1 = container.Resolve<OperationMain>(); op1.PlayMedia(); //普通方式 OperationMain op2 = new OperationMain(new Player(), new MediaFile()); op2.PlayMedia(); }
注入方式:
依赖注入的方式有很多:
- 构造器注入(Constructor Injection):Ioc容器会智能地选择选择和调用适合的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,Ioc容器在调用构造函数之前解析注册的依赖关系并自行获得相应参数对象;
- 属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,Ioc容器会自动初始化该属性;
- 方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,Ioc容器会自动调用该方法
编写实例说明依赖注入的方式:
接口IA,IB,IC,ID和具体类A,B,C,D。
public interface IA{ } public interface IB{} public interface IC { } public interface ID { } public class A : IA { public IB B { get; set; } [Dependency] public IC C { get; set; } public ID D { get; set; } public A(IB b) { this.B = b; } [InjectionMethod] public void Initialize(ID d) { this.D = d; } } public class B : IB{} public class C : IC { } public class D : ID { }
客户端调用方式:
IUnityContainer container = new UnityContainer(); container.RegisterType<IA, A>(); container.RegisterType<IB, B>(); container.RegisterType<IC, C>(); container.RegisterType<ID, D>(); A a = container.Resolve<IA>() as A; if (null != a) { Console.WriteLine("a.B == null ? {0}", a.B == null ? "Yes" : "No"); Console.WriteLine("a.C == null ? {0}", a.C == null ? "Yes" : "No"); Console.WriteLine("a.D == null ? {0}", a.D == null ? "Yes" : "No"); }
运行结果:
自我理解:
依赖注入,从深层次上还是不能理解其本质。此处介绍其用法,大致可分为三步:
1、定义一个container,
IUnityContainer container = new UnityContainer();
2、接口和实现类的注册,
container.RegisterType<ILogger, Logger>();
3、生成对象
ILogger logger = container.Resolve<ILogger>();
完
精彩摘抄:
IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
IoC很好的体现了面向对象设计法则之一—— 好莱坞法则:“别找我们,我们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。
DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。
IoC和DI由什么关系呢?其实它们是同一个概念的不同角度描述,由于控制反转概念比较含糊(可能只是理解为容器控制对象这一个层面,很难让人想到谁来维护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,相对IoC 而言,“依赖注入”明确描述了“被注入对象依赖IoC容器配置依赖对象”。
网上摘抄:
一、
大多数面向对象编程语言,在调用一个类的时候,先要实例化这个类,生成一个对象。
如果你在写一个类,过程中要调用到很多其它类,甚至这里的其它类,也要“依赖”于更多其它的类,那么可以想象,你要进行多少次实例化。
这就是“依赖”的意思。
依赖注入,全称是“依赖注入到容器”, 容器(IOC容器)是一个设计模式,它也是个对象,你把某个类(不管有多少依赖关系)放入这个容器中,可以“解析”出这个类的实例。
所以依赖注入就是把有依赖关系的类放入容器(IOC容器)中,然后解析出这个类的实例
二、
假如有一个 船(C)类 ,一个 桨(J) 类,
class C{ J j = new J() ; }
如果船要干什么事,肯定需要浆的参与。所以是十分 “依赖”浆;
出了需求需要重构:这时候我们需要控制浆的长度为10在构造方法中。我们需要这么写;
class C{ J j = new J(10) ; }
一个特性需要修改浆构造方法,又需要修改船其中的new J()方法。这时候就设计者就思考,为什么我们加入一个特性需要更改两个类中代码(这也就是耦合度高)!
所以我们要解耦要依赖注入;
常用解耦方式:
构造方法注入
如下:我重构代码的时候在也不用看哪里的浆还是短的了!因为船构造方法依赖了浆。任你浆怎么设计,我用的时候传一个浆进来即可。(下层依赖上层,用的时候传入,而不是针对下层去修改)
class C{ J j ; public c(J j) { this.j = j; }; }
工厂模式注入
工厂模式 Human 人 去注入; 工厂类如下
Class Human { J j =new J(); J getJ() { return j ; } }
此时如下:不管你怎么改浆,改成100米与船都无关,他只要依赖Human,
一千个船修改浆需求我只修改Human类中方法便可。(核心业务逻辑需要依赖的类实例化交给第三方类来实现注入。)
Class C { J j ; Human h = new Human; j=Human.getJ(); }
框架注入(本质还是工厂设计模式的具体实现)
本质也是第三方依赖注入,但是这个第三方可以脱离类。将对象依赖映射信息存储在容器一般为.xml 或者特定的对象中,并实现动态的注入。
推荐两篇相当精彩实用的博文:
引用: