昨天花了一天时间,把IOC/DI的相关文章以及Unity相关的一些文章基本在园子里搜了个遍
先给出几篇不错的文章链接:
Unity Application Block官方网址 http://www.codeplex.com/unity
吕震宇整理的[Object Builder Application Block] http://www.cnblogs.com/zhenyulu/articles/641728.html
吕震宇[你真的了解Ioc与AOP吗?] http://www.cnblogs.com/zhenyulu/articles/233966.html
坚强2002翻译的[Inversion of Control Containers and the Dependency Injection pattern] http://www.cnblogs.com/me-sa/archive/2008/07/30/IocDI.html
赤脚小子的[unity系列] http://www.cnblogs.com/kebixisimba/archive/2008/05/19/1202467.html
NEE's [Unity 配置:typeConverter的使用] http://www.cnblogs.com/nickyao/archive/2008/05/04/1181804.html
Warmth & Chilliness的[Unity -- .NET下的原生Ioc框架,让一部分人先用起来] http://www.cnblogs.com/think8848/archive/2008/10/25/1319616.html
基本上把上面的这些个文章全耐着性子看完,相关知识点的“扫盲”工作也差不多完成了
这里只是把我练习的一个例子贴在这里,并发表一些个人粗浅的看法
应用场景:随便给一些数字,要求对这些数字进行一项基本的数据运算(例子中只实现了加法/乘法)
先添加对Microsoft.Practices.Unity.dll的引用
准备测试用的接口和类:
///
<summary>
///
接口
///
</summary>
public
interface
Icalculate
{
int
Calculate(
params
int
[] a);
double
Calculate(
params
double
[] a);
string
GetOperationName();
}
///
<summary>
///
加法运算
///
</summary>
public
class
Addtive : Icalculate
{
///
<summary>
///
注意:至少要有一个构造函数,否则用配置文件方式Resolve
<Icalculate>
时会提示:Icalculate是一个接口,没有构造函数,所以不能创建实例云云,但有趣的是用硬编码方式却可以成功
///
</summary>
public
Addtive() { }
public
int
Calculate(
params
int
[] a)
{
int
Result
=
0
;
foreach
(
int
x
in
a)
{
Result
+=
x;
}
return
Result;
}
public
double
Calculate(
params
double
[] a)
{
double
Result
=
0.0
;
foreach
(
double
x
in
a)
{
Result
+=
x;
}
return
Result;
}
public
string
GetOperationName()
{
return
"
加法
"
;
}
}
///
<summary>
///
乘法运算
///
</summary>
public
class
Multiplication : Icalculate
{
public
Multiplication() { }
public
int
Calculate(
params
int
[] a)
{
int
Result
=
1
;
foreach
(
int
x
in
a)
{
Result
*=
x;
}
return
Result;
}
public
double
Calculate(
params
double
[] a)
{
double
Result
=
1.0
;
foreach
(
double
x
in
a)
{
Result
*=
x;
}
return
Result;
}
public
string
GetOperationName()
{
return
"
乘法
"
;
}
}
///
<summary>
///
(四则)运算管理器
///
</summary>
public
class
CalcManager
{
private
Icalculate _calc;
public
CalcManager(Icalculate IC)
{
_calc
=
IC;
}
//
[InjectionMethod]
//
public void SetCalculate(Icalculate IC) {
//
_calc = IC;
//
}
public
void
Compute(
params
int
[] a)
{
string
_paramName
=
""
;
foreach
(
int
x
in
a)
{
_paramName
+=
x.ToString()
+
"
,
"
;
}
_paramName
=
_paramName.Trim(
'
,
'
);
Console.WriteLine(
"
{0} {1}计算结果:{2}
"
, _paramName, _calc.GetOperationName(), _calc.Calculate(a));
}
public
void
Compute(
params
double
[] a)
{
string
_paramName
=
""
;
foreach
(
double
x
in
a)
{
_paramName
+=
x.ToString()
+
"
,
"
;
}
_paramName
=
_paramName.Trim(
'
,
'
);
Console.WriteLine(
"
{0} {1}计算结果:{2}
"
, _paramName, _calc.GetOperationName(), _calc.Calculate(a));
}
}
为了对比,我们先用传统方式来调用试下:
static
void
Main(
string
[] args)
{
#region
不用依赖注入的传统方式
CalcManager CM
=
new
CalcManager(
new
Addtive());
CM.Compute(
1
,
2
,
3
,
4
,
5
);
//
计算1,2,3,4,5的和
//
CM = new CalcManager(new Multiplication());
//
CM.Compute(1, 2, 3, 4, 5);
//
计算1,2,3,4,5的乘积
#endregion
Console.ReadLine();
}
虽然简单易懂,但细想一下可扩展性并不高,如果以后又增加了除法,平方,减法...等一系列算法,不是每次都要这一段代码吗?原因就是接口,算法实体类,调用程序之间的耦合性太高
接下来,我们用Unity换一种写法:
using
System;
using
Microsoft.Practices.Unity;
using
Microsoft.Practices.Unity.Configuration;
using
System.Configuration;
static
void
Main(
string
[] args)
{
#region
使用依赖注入(硬编码方式)
IUnityContainer container
=
new
UnityContainer();
container.RegisterType
<
Icalculate, Addtive
>
()
//
注入加法类
.RegisterType
<
CalcManager
>
();
//
注入管理器
CalcManager CM
=
container.Resolve
<
CalcManager
>
();
//
取得CalcManager的实例
CM.Compute(
1.1
,
2.9
,
3.1
,
4
,
5
);
//
container.RegisterType<Icalculate, Multiplication>();
//
继续注入乘法类
//
CM = container.Resolve<CalcManager>();
//
CM.Compute(1, 2, 3, 4, 5);
#endregion
Console.ReadLine();
}
单从代码上看,只不过换了种写法和思路,但仍然属于“硬编码”的方式,如果要增加其它算法或换成其它算法,一样还是要改这段代码.(貌似纯属瞎折腾?呵呵)
下面切入正题,Unity除了这种硬编码方式,还允许把注入规则/映射写到配置文件里
先修改App.Config,内容大致如下:
<?
xml version
=
"
1.0
"
encoding
=
"
utf-8
"
?>
<
configuration
>
<
configSections
>
<
section name
=
"
unity
"
type
=
"
Microsoft.Practices.Unity.Configuration.UnityConfigurationSection, Microsoft.Practices.Unity.Configuration, Version=1.2.0.0, Culture=neutral
"
/>
</
configSections
>
<
unity configSource
=
"
config\DI.config
"
/>
</
configuration
>
同时再新建一个config目录,把DI.config文件放在该目录下,内容:
<?
xml version
=
"
1.0
"
?>
<
unity
>
<
typeAliases
>
<
typeAlias alias
=
"
ICalc
"
type
=
"
UnityStudy.Icalculate, UnityStudy
"
/>
<
typeAlias alias
=
"
Add
"
type
=
"
UnityStudy.Addtive, UnityStudy
"
/>
<
typeAlias alias
=
"
Mul
"
type
=
"
UnityStudy.Multiplication, UnityStudy
"
/>
<
typeAlias alias
=
"
CM
"
type
=
"
UnityStudy.CalcManager, UnityStudy
"
/>
</
typeAliases
>
<
containers
>
<
container
>
<
types
>
<
type type
=
"
ICalc
"
mapTo
=
"
Mul
"
/>
<!--
结实验,下面这一行加不加程序都能运行,只要确保CalcManager中有一个参数为Icalculate的构架函数或(注入)设置方法就行[参看CalcManager中注释掉的部分],Unity在这一点上确实比较“智能”
-->
<
type type
=
"
CM
"
/>
</
types
>
</
container
>
</
containers
>
</
unity
>
调用代码再换一种写法:
static
void
Main(
string
[] args)
{
#region
使用依赖注入(配置文件方式)
IUnityContainer container
=
new
UnityContainer();
UnityConfigurationSection section
=
(UnityConfigurationSection)ConfigurationManager.GetSection(
"
unity
"
);
section.Containers.Default.Configure(container);
CalcManager CM
=
container.Resolve
<
CalcManager
>
();
CM.Compute(
1
,
2
,
3
,
4
,
5
);
#endregion
Console.ReadLine();
}
这回仔细看下,代码中已经完全找不到Addtive,Multiplication等这些具体的类名了,整个程序完全依赖于配置文件中的相关节点(其实OO的角度来讲,是基于接口Icalculate的编程以及不同对象的组合让这一切成为可能)。
如果我们要把乘法运算换成加法运算,太容易了,把DI.config中的
<type type="ICalc" mapTo="Mul"/>
换成
<type type="ICalc" mapTo="Add"/>
原来的调用代码一行都不用改!
最后:Unity除了实现IOC/DI之外还有一些其它用途,比如:实现单件模式(而且这种实现方式更灵活,比如我们可以让任何一个普通的类,在容器的生命周期内仅返回一个实例,这是传统的单件模式中"把类硬编码定死为单件实例"无法做到的)