IOC即Inversion of control,控制反转的意思,是程序解耦的一种设计原则,核心思想是依赖容器注入对象的引用,依赖注入则是这种设计的具体实现方式,从语义上来说这两个还不能算一样。但从控制反转顾名思义是对控制的反转,那控制的又是什么呢?我们应该知道控制的是对象,想到对象,我们应该会想到工厂,工厂是用来就是生成对象的地方,有人说IOC就是工厂模式的升级,其实我感觉只能说的他们的原理很像,这里就借工厂模式来说一下IOC吧。
先上一段工厂模式的代码吧
代码结构:
工厂代码:
//user实体
namespace IOC
{
public class User
{
public int ID { get; set; }
public string Name { get; set; }
}
}
//实体接口中定义一个方法
namespace IOC
{
public interface IUser
{
void Create(User user);
}
}
//sqlserver对iuser中create的实现
namespace IOC.SqlServer
{
class SqlUser : IUser
{
public void Create(User user)
{
Console.WriteLine("Create user by SqlServer");
}
}
}
//oracle的实现
namespace IOC.Oracle
{
class OraUser : IUser
{
public void Create(User user)
{
Console.WriteLine("Create user by Oracle");
}
}
}
//下面是工厂类,这里可以灵活切换数据库方便扩展
namespace IOC
{
class Factory
{
static readonly string DBName="SqlServer";
public static IUser Create()
{
IUser user;
switch (DBName)
{
case "SqlServer":
user = new SqlServer.SqlUser();
break;
default:
user = new Oracle.OraUser();
break;
}
return user;
}
}
}
//下面是最终的输出窗口了
namespace IOC
{
class Program
{
static void Main(string[] args)
{
User user = new User();
IUser iu = Factory.Create();
iu.Create(user);
Console.ReadLine();
}
}
}
工厂方式很好的实现了调用和对象的解耦,我们可以试着以IOC的角度去理解这段代码。谁是容器呢?工厂就是容器,怎么依赖注入呢?其实是容器(也就是工厂)在Main中注入了对SqlServer的依赖,Main中被动的去创建SqlServer对象,那么是不是到这里就完了?答案肯定不是了,如果真是这样还要IOC做什么,直接工厂就是了,上面的实现有个缺点,细心的朋友应该会想,我要是想用换oracle数据库呢?看下面这段代码
static readonly string DBName="SqlServer";
public static IUser Create()
{
IUser user;
switch (DBName)
{
case "SqlServer":
user = new SqlServer.SqlUser();
break;
default:
user = new Oracle.OraUser();
break;
}
return user;
}
如果我换oracle的话是不是得修改DBName?如果我想加mysql的逻辑呢?是不是改的更多,改了工厂类,封闭做的好吗?答案显然易见,这时候我们就需要做出调整,IOC不一样的地方这时候就该显现了。下面总结了三点
一:增加XDataBase.xml
<?xml version="1.0" encoding="utf-8" ?>
<objects>
<object id="SqlServer" type="IOC.SqlServer.SqlUser"></object>
<object id="Oracle" type="IOC.Oracle.OraUser"></object>
</objects>
填充容器中实例话的对象集
二:工厂类中实现容器概念
class Container
{
Dictionary<string, object> dics = new Dictionary<string, object>();
public Factory(string fileName){
InstanceObjects(fileName);
}
/// <summary>
/// 创建容器
/// </summary>
/// <param name="fileName"></param>
public void InstanceObjects(string fileName)
{
//读取xml文件中的对象集合并加入Dictionary
XElement xE = XElement.Load(fileName);
var objs = from obj in xE.Elements("object") select obj;
dics = objs.ToDictionary(k => k.Attribute("id").Value,
v =>
{
string typeName = v.Attribute("type").Value;
Type type = Type.GetType(typeName);
return Activator.CreateInstance(type);
});
}
/// <summary>
/// 获取对象
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
public object GetObject(string id)
{
object obj = null;
if(dics.Keys.Contains(id))
{
obj = dics[id];
}
return obj;
}
}
这里利用反射将所用xml中的对象加入Dictionary,同时提供一个获取对象的方法:GetObject,客户端可以根据自己的需要自动切换对象
三:配置文件
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<!--<add key="TypeName" value="IOC.SqlServer.SqlUser"/>-->
<add key="TypeName" value="IOC.Oracle.OraUser"/>
<add key="AssemblyName" value="IOC"/>
</appSettings>
</configuration>
每次换数据库只需要更改配置文件即可
四:客户端
static readonly string TypeName = ConfigurationManager.AppSettings["TypeName"];
static void Main(string[] args)
{
User user = new User();
Container con= new Container(@"D:\项目\设计模式练习\IOC\IOC\XDataBase.xml");
IUser iu = (IUser)con.GetObject(TypeName);
iu.Create(user);
Console.ReadLine();
}
客户端可以根据自己的需要灵活的切换数据库对象,工厂类不需要在做任何修改,如果想再增加Mysql的逻辑,只用新增Mysql类+修改xml文件即可