Ioc最佳实践
Ioc(依赖倒转)概念:不创建对象,但是描述创建它们的方式。在代码中不直接与对象和服务连接,但在配置文件中描述哪一个组件需要哪一项服务。容器负责将这些联系在一起。也就是说在项目中相关联类实例化的时候统一进行管理,客户端不需要关注类之间关联,只需要通过名称,就能获取实例化对象,记得在Pet shop中对于DAL层的实例是通过抽象工厂,通过客户端配置web.config文件反射得到,当然这样也能实现实例化对象的解耦,但是这种方式也有很多弊端,比如:反射性能、产品类复杂导致实例化麻烦等,Ioc就是解决这些问题,所以说Ioc是抽象工厂的升级。
参考院子(李会军)老师的文章,先把.net平台下的几种优秀的IOC框架做一个总结:
第一种:Castle
Castle中包含了一组开发框架,它里面的IOC容器是Windsor,目前Castle已经发布了RC1版本,其中Windsor已经是RC3了。在windsor中,提出了自动装配的概念,由容器来自动管理组件之间的依赖关系,无需用户去编写XML配置文件或者通过Attribute来指定容器之间的依赖关系。这样在使用伤非常简单,同样也带了一些问题,作为开发人员的我们无法控制组件依赖关系。如下面的xml配置文件,仅仅是设定了一个参数而已:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<components>
<component id="txtLog">
<parameters>
<target>log.txt</target>
</parameters>
</component>
</components>
</configuration>
简单使用:
声明一个日志接口:
using System;
namespace CastleIoc1
{
/// <summary>
/// 编写:Terrylee
/// 出处:http://terrylee.cnblogs.com
/// </summary>
public interface ILog
{
void Write(string MsgStr);
}
}
日志实现类,记录日志:
using System;
namespace CastleIoc1
{
/// <summary>
/// 编写:Terrylee
/// 出处:http://terrylee.cnblogs.com
/// </summary>
public class TextFileLog : ILog
{
private string _target;
private ILogFormatter _format;
public TextFileLog(string target,ILogFormatter format)
{
this._target = target;
this._format = format;
}
public void Write(string MsgStr)
{
string _MsgStr = _format.Format(MsgStr);
_MsgStr += _target;
//Output Message
Console.WriteLine("Output "+_MsgStr);
}
}
}
操作日志接口:
using System;
namespace CastleIoc1
{
/// <summary>
/// 编写:Terrylee
/// 出处:http://terrylee.cnblogs.com
/// </summary>
public interface ILogFormatter
{
string Format(string MsgStr);
}
}
操作日志实现类:
using System;
namespace CastleIoc1
{
/// <summary>
/// 编写:Terrylee
/// 出处:http://terrylee.cnblogs.com
/// </summary>
public class TextFormatter : ILogFormatter
{
public TextFormatter()
{
}
public string Format(string MsgStr)
{
return "[" + MsgStr + "]";
}
}
}
当我们客户端实例的时候,我们可以看到我们的日志实现类依赖者日志操作类,也就是说我们实例化的时候,需要先实例化日志操作类,然后传递给日志实现类,但是在Ioc中,我们解决的就是这个问题,实现这两个类的解耦,因为在日志实现类需要传入一个“日志地址”的参数,所以在BasicUsage.xml中定义该参数
using System;
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;
namespace CastleIoc1
{
/// <summary>
/// 编写:Terrylee
/// 出处:http://terrylee.cnblogs.com
/// </summary>
public class App
{
public static void Main()
{
//建立容器
IWindsorContainer container = new WindsorContainer( new XmlInterpreter("http://www.cnblogs.com/BasicUsage.xml") );
//加入组件
container.AddComponent( "txtLog",
typeof(ILog), typeof(TextFileLog) );
container.AddComponent( "format",
typeof(ILogFormatter), typeof(TextFormatter) );
//获取组件
ILog log = (ILog) container["txtLog"];
//使用组件
log.Write("First Castle IOC Demo");
Console.ReadLine();
}
}
}
这样我们就能实现两个类的解耦,使用的时候,只需要从container中获取强制转换下就行。这里有一点需要记住,我们添加的组件的时候,我们在xml文件中定义的ID名称已经要和我们添加的名称相同,如上面的txtLog..
第二种:Spring.Net
Sping.Net是从Java的Spring Framework移植过来的,现在的版本应该是Spring.NET 1.0.2,正好和前面说的Casle相反,Spring.Net推崇的做法是使用配置文件来管理组件之间的依赖关系,当然它支持自动装配,不过不推荐使用。这样配置文件的方式,带来的问题是当我们的项目非常大的时候,配置文件非常的繁琐,手工配置会变的很复杂。需要指定每一个组件以及他们之间的依赖关系。
新建接口,个人信息操作
using System;
namespace Spring.net
{
public interface IPersonInfo
{
string save();
}
}
个人信息操作实现类
using System;
namespace Spring.net
{
public class PersonInfo : IPersonInfo
{
public string save()
{
return ("您已经保存了个人信息");
}
}
}
个人信息操作接口:
using System;
namespace Spring.net
{
public interface IpersonInfoManger
{
void MyManger(string _name, IPersonInfo _Iperson);
}
}
个人信息操作实现类:
using System;
namespace Spring.net
{
public class personInfoManger:IpersonInfoManger
{
public void MyManger(string _name, IPersonInfo _Iperson)
{
Console.WriteLine(string.Format("{0}对信息进行了保存:{1}",_name,_Iperson.save()));
}
}
}
在app.config中配置我们的这两个方法:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object id="myObject" type="Spring.net.PersonInfo, Spring.net" singleton="true">
</object>
<object id="myObject2" type="Spring.net.personInfoManger, Spring.net" singleton="true">
</object>
</objects>
</spring>
</configuration>
看客户端实现方法:
using System;
using Spring.Context.Support;
using Spring.Context;
namespace Spring.net
{
class Program
{
static void Main(string[] args)
{
//普通方法
IpersonInfoManger IIM = new personInfoManger();
IIM.MyManger("超人", new PersonInfo());
//IOC方法
IApplicationContext ctx = ContextRegistry.GetContext();
IPersonInfo dao = ctx.GetObject("myObject") as IPersonInfo;
IpersonInfoManger infoManger = ctx.GetObject("myObject2") as IpersonInfoManger;
if (dao != null)
{
Console.WriteLine("----------------------------");
infoManger.MyManger("超人2", dao);
}
Console.ReadLine();
}
}
}
至此已经是实现了一个spring.net的简单的Ioc应用。
第三种,微软的Unity实现,作为轻量级的开发模型,微软肯定也是不甘落后的,下面看一下在Unity下该如何配置IOC应用。
项目中添加引用:
简单的设计一个模型,一个IMan(人类)接口,一个IWeapon(武器)接口,很显然这两者是耦合在一起的,我们通IOC对两者进行解耦
看设计图
看几个关键类:
using System;
using System.Text;
namespace UnityMyDemo
{
public class AmericaMan:IMan
{
#region IMan 成员
private IWeapon myWeapon;
public AmericaMan(IWeapon _Weapon)
{
myWeapon = _Weapon;
}
public string GetName()
{
return "美国人武器" + myWeapon.Atter();
}
#endregion
}
}
同样在config文件中对相关联的类进行配置:
<?xml version="1.0"?>
<configuration>
<configSections>
<section name="unity" type="Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,
Microsoft.Practices.Unity.Configuration"/>
</configSections>
<!--unity声明段,加入xmlns后vs会下载语法规范并语法提示-->
<unity xmlns="http://schemas.microsoft.com/practices/2010/unity">
<alias alias="singleton" type="Microsoft.Practices.Unity.ContainerControlledLifetimeManager,
Microsoft.Practices.Unity"/>
<alias alias="external" type="Microsoft.Practices.Unity.ExternallyControlledLifetimeManager,
Microsoft.Practices.Unity"/>
<namespace name="UnityMyDemo"/>
<!--命名空间-->
<assembly name="UnityMyDemo"/>
<!--程序集名称-->
<container name="myContaion">
<register type="IWeapon" mapTo="Gun" name="AmericaWeapon"/>
<!--声明类型映射-->
<register type="IWeapon" mapTo="Sword" name="chinaWeapon"/>
<!--声明类型映射-->
<register type="IMan" mapTo="ChinaMan" name="ChinaManFactory">
<lifetime type="singleton"></lifetime>
<!--生命周期为单例-->
<constructor>
<!--构造注入-->
<param name="_Weapon">
<!--构造函数参数-->
<dependency name="chinaWeapon"/>
<!--自动匹配类型-->
</param>
</constructor>
</register>
<register type="IMan" mapTo="AmericaMan" name="AmericaManFactory">
<lifetime type="singleton"></lifetime>
<!--生命周期为单例-->
<constructor>
<!--构造注入-->
<param name="_Weapon">
<!--构造函数参数-->
<dependency name="AmericaWeapon"/>
<!--自动匹配类型-->
</param>
</constructor>
</register>
</container>
</unity>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup>
</configuration>
文档很清楚..不做解释..需要说明点就是可以在这里直接声明unity结点,而代替代码的注册
看应用类:
class Program
{
static void Main(string[] args)
{
//传统用法
Console.WriteLine("\n普通方法注册");
IWeapon Weapon = new Sword();
IMan Man = new ChinaMan(Weapon);
Console.WriteLine(Man.GetName());
Weapon = new Gun();
Man = new AmericaMan(Weapon);
Console.WriteLine(Man.GetName());
Console.WriteLine("\nIOC方法注册");
//IOC方法
Console.WriteLine(IOCHelper.Get("myContaion").Resolve<ChinaMan>("ChinaManFactory").GetName());
Console.WriteLine(IOCHelper.Get("myContaion").Resolve<AmericaMan>("AmericaManFactory").GetName());
Console.Read();
}
}
可以看到的是IOC方法根本不需要知道该类所应用的接口是谁,直接替换成工厂模式的需要知道抽象工厂接口
上传下源码