IOC(Inversion of Control)控制反转——为了符合依赖倒置原则,也就是高层模块不依赖于低层模块,他们都应该依赖于抽象;抽象不应该依赖于具体,具体依赖于抽象。例如小时候一家一户水井,人们喝水依赖水井和地下水,如果搬迁就要重新打井,水污染就没水喝,这是反依赖倒置;引入自来水厂以后,人们随意搬迁都能喝到水,接一根水管就行,当地下水污染,自来水厂也可以选择其他水源。也就是使用接口,但是总归接口是要被实现的,耦合也就不可避免的产生了。
通过IOC模式可以解决这个问题,把耦合放到XML文件,通过一个容器在需要的时候再把依赖关系形成,即把需要的接口实现注入到需要它的类。反射应该也是IOC的一个实现的方法。IOC的特点是通过文本的配置文件进行应用程序组件间相互关系的配置,不用修改和编译具体的代码。
依赖注入(Dependency Injection)是IOC的一种运行模式,把需要的接口实现注入到需要它的类中。 依赖注入的方法有很多:
1.构造器注入(Constructor Injection):Ioc容器会智能地选择选择和调用适合的构造函数以创建依赖的对象。如果被选择的构造函数具有相应的参数,Ioc容器在调用构造函数之前解析注册的依赖关系并自行获得相应参数对象;
2.属性注入(Property Injection):如果需要使用到被依赖对象的某个属性,在被依赖对象被创建之后,Ioc容器会自动初始化该属性;
3.方法注入(Method Injection):如果被依赖对象需要调用某个方法进行相应的初始化,在该对象创建之后,Ioc容器会自动调用该方法。
Unity提供了对象的容器,那么这个容器是如何进行索引的呢?也就是说,容器内的单元是如何标识的呢?在Unity中,标识主要有两种方式, 一种是直接使用接口(或者基类)作为标识键,另一种是使用接口(或者基类)与名称的组合作为标识键,键对应的值就是具体类。
第一种:使用接口(或者基类)作为标识键:
//IWaterTool就是作为标识键,也可以使用基类或是抽象类作为标示,获取注册对象:container.Resolve<IWaterTool>()
container.RegisterType< IWaterTool, PressWater>();
复制代码
第二种:如果一个Ioc容器容器里面注册了多个接口或是基类标示,我们再这样获取就不知道注册的是哪一个?怎么解决,就是用接口或是基类与名称作为标识键,示例代码如下:
container.RegisterType<IWaterTool, PressWater>("WaterTool1");//注册依赖对象WaterTool1
container.RegisterType<IWaterTool, PressWater>("WaterTool2");//注册依赖对象WaterTool2
IWaterTool wt = container.Resolve<IWaterTool>("WaterTool1");//返回依赖对象WaterTool1
var list = container.ResolveAll<IWaterTool>();//返回所有注册类型为IWaterTool的对象
复制代码
在配置文件的映射关系加入name(待考证)
<register type="IOCDependency.IWaterTool,IOCDependency" name="WaterTool1" mapTo="IOCDependency.PressWater,IOCDependency" />
复制代码
构造器注入例子如下: 使用构造器注入需要在在构造函数中传递一个抽象参数,Ioc会自动解析具象所依赖的抽象并注册给具象,还是用上篇喝水作为示例。
点击“管理NuGet程序包”,安装“Unity” 分别定义人、水源和获取水源方式的接口。
/// <summary>
/// 人接口:喝水,无返回值
/// </summary>
public interface IPeople
{
void DrinkWater();
}
/// <summary>
/// 获取水方式接口
/// </summary>
public interface IWaterTool
{
IWater returnWater();
}
/// <summary>
/// 水接口
/// </summary>
public interface IWater
{ }
复制代码
创建村民类,实现人的接口。
/// <summary>
/// <summary>
/// 村民
/// </summary>
public class VillagePeople : IPeople
{
IWaterTool _pw;
public VillagePeople(IWaterTool pw)
{
_pw = pw;//传递抽象参数
}
//实现接口
public void DrinkWater()
{
IWater uw = _pw.returnWater();
if (uw != null)
{
Console.WriteLine("水好甜啊!!!");
Console.ReadLine();
}
}
}
复制代码
创建地下水类,实现水源接口。
/// <summary>
/// 地下水
/// </summary>
public class UndergroundWater : IWater
{
}
复制代码
创建压水井类,实现获取水方式接口。
/// <summary>
/// 压水井
/// </summary>
public class PressWater : IWaterTool
{
public IWater returnWater()
{
return new UndergroundWater();
}
}
复制代码
{
static void Main(string[] args)
{
UnityContainer container = new UnityContainer();//创建一个Unity容器
//在容器中注册一种类型,即决定用什么用什么做水源,需要注册一下。键是IWaterTool,值为PressWater
container.RegisterType< IWaterTool, PressWater>();
//Resolve:将容器的类型输送到VillagePeople
IPeople people = container.Resolve<VillagePeople>();
people.DrinkWater();
}
}
复制代码
也可以使用非泛型注入,代码如下:
static void Main(string[] args)
{
UnityContainer container = new UnityContainer();//创建容器
container.RegisterType(typeof(IWaterTool), typeof(PressWater));//注册依赖对象
IPeople people = (IPeople)container.Resolve(typeof(VillagePeople));//返回调用者
people.DrinkWater();//喝水
}
复制代码
属性注入例子如下: 属性注入只需要在属性字段前面加[Dependency]标记就行了,这次不使用RegisterType方法注册,而是使用配置文件注册。下面用UnityConfigurationSection的Configure方法加载配置文件注册: 点击“管理NuGet程序包”,安装“Unity.Container”
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<!-- 声明unity配置节,type一定要写对-->
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Unity.Configuration" />
</configSections>
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<!-- 容器 -->
<containers>
<container name="defaultContainer">
<!--映射关系,type=命名空间,程序集-->
<register type="IOCDependency.IWaterTool,IOCDependency" mapTo="IOCDependency.PressWater,IOCDependency" />
<register type="IOCDependency.IPeople,IOCDependency" mapTo="IOCDependency.VillagePeople,IOCDependency" />
</container>
</containers>
</unity>
</configuration>
复制代码
改改村民和主程序的代码
/// <summary>
/// 村民
/// </summary>
public class VillagePeople : IPeople
{
[Dependency]//属性注入
public IWaterTool _pw { get; set; }
public void DrinkWater()
{
Console.WriteLine(_pw.returnWater());
Console.ReadKey();
}
}
复制代码
static void Main(string[] args)
{
UnityContainer container = new UnityContainer();//创建容器
UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
configuration.Configure(container, "defaultContainer");;
//container.Config("Unity.config");
IPeople people = container.Resolve<IPeople>();//返回调用者
people.DrinkWater();//喝水
}
复制代码
方法注入例子如下: 方法注入和属性方式使用一样,方法注入只需要在方法前加[InjectionMethod]标记就行了,从方法注入的定义上看,只是模糊的说对某个方法注入,并没有说明这个方法所依赖的对象注入,所依赖的对象无非就三种:参数、返回值和方法内部对象引用,下面做一个示例:
/// <summary>
/// 村民
/// </summary>
public class VillagePeople : IPeople
{
public IWaterTool tool;//我是对象引用
public IWaterTool tool2;//我是参数
public IWaterTool tool3;//我是返回值
[InjectionMethod]
public void DrinkWater()
{
if (tool == null)
{ }
}
[InjectionMethod]
public void DrinkWater2(IWaterTool tool2)
{
this.tool2 = tool2;
}
[InjectionMethod]
public IWaterTool DrinkWater3()
{
return tool3;
}
}
复制代码
static void Main(string[] args)
{
UnityContainer container = new UnityContainer();//创建容器
UnityConfigurationSection configuration = (UnityConfigurationSection)ConfigurationManager.GetSection(UnityConfigurationSection.SectionName);
configuration.Configure(container, "defaultContainer");
VillagePeople people = container.Resolve<IPeople>() as VillagePeople;//返回调用者
Console.WriteLine("people.tool == null(引用) ? {0}", people.tool == null ? "Yes" : "No");
Console.WriteLine("people.tool2 == null(参数) ? {0}", people.tool2 == null ? "Yes" : "No");
Console.WriteLine("people.tool3 == null(返回值) ? {0}", people.tool3 == null ? "Yes" : "No");
Console.ReadKey();
}
复制代码
其实我们理解的方法注入就是对参数对象的注入(目前不理解这句话。)
最后
作者:Java小叮当
链接:https://juejin.cn/post/6954607668874969124
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。