你真的了解Ioc与AOP吗?

作为一个应用系统,代码复用至关重要。如果在你的设计中,类与类存在很强的相互关联,那么你会发现在重用这些组件时就存在很严重的问题。在 Step1到Step3-Reflection的例子中,我们试图 利用“针对接口编程”以及自己设计的Ioc对系统进行解耦。在Step3到Step5的例子中,我们将利用Spring.net提供的Ioc框架,轻松完 成解耦以及系统改造等工作。

一、类之间的依赖

我们的第一个例子主要用于说明程序的基本构造,并且作为一个反面典型,引出为什么要解耦,以及如何下手。在这个例子中,我们将创建三个程序集,分别是MainApp.exe、HelloGenerator.dll以及SayHello.dll。它们之间的关系如下图所示:

 

 

 


 

 

 

HelloGenerator类根据提供的姓名产生一个问候字符串,代码如下:

using System;

namespace IocInCSharp

{

public class EnHelloGenerator

{

public string GetHelloString(string name)

{

return String.Format("Hello, {0}", name);

}

}

}







SayHello类持有一个对EnHelloGenerator的引用,并负责将生成出来的问候字符串打印出来。

using System;

namespace IocInCSharp

{

public class SayHello

{

private EnHelloGenerator _helloGen;

public EnHelloGenerator HelloGenerator

{

get { return _helloGen; }

set { _helloGen = value; }

}

public void SayHelloTo(string name)

{

if(_helloGen != null)

Console.WriteLine(_helloGen.GetHelloString(name));

else

Console.WriteLine("Client.hello is not initialized");

}

}

}





MainApp.exe负责完成对象的创建、组装以及调用工作:

using System;

namespace IocInCSharp

{

public class MainApp

{

public static void Main()

{

SayHello sayHello = new SayHello();

sayHello.HelloGenerator = new EnHelloGenerator();

sayHello.SayHelloTo("zhenyulu");

}

}

}





在这个设计中,组件与组件之间、类与类之间存在着紧密的耦合关系。SayHello类中的_helloGen字段类型为 EnHelloGenerator,这将导致我们很难给它赋予一个其它的HelloGenerator(例如CnHelloGenerator,用于生成 中文问候语)。另外MainApp也严重依赖于SayHello.dll以及HelloGenerator.dll,在程序中我们可以看到类似new SayHello();new EnHelloGenerator();的命令。

这种紧密的耦合关系导致组件的复用性降低。试想,如果想复用SayHello组件,那么我们不得不连同HelloGenerator一同拷贝过去, 因为SayHello.dll是依赖与HelloGenerator.dll的。解决这个问题的办法就是“针对抽象(接口)”编程 (依赖倒置原则)。这里的抽象既包括抽象类也包括接口。我不想过多的去谈抽象类和接口的区别,在后续的例子中我们将使用接口。由于接口在进行“动态代理” 时仍能保持类型信息,而抽象类可能由于代理的原因导致继承关系的“截断”(如MixIn等)。除此之外,对于单继承的C#语言而言,使用接口可以拥有更大 的弹性。

二、接口依赖

既然类之间的依赖导致耦合过于紧密,按照《设计模式》的理论,我们要依赖于接口。但是人们往往发现,仅仅依赖于接口似乎并不能完全解决问题。我们从上面的例子中抽象出接口后,组件间的依赖关系可能变成如下图所示:

 

 

 


 

 

 

经过改造后,SayHello不再依赖于具体的HelloGenerator,而是依赖于IHelloGenerator接口,如此一来,我们可以 动态的将EnHelloGenerator或是CnHelloGenerator赋给SayHello,其打印行为也随之发生改变。接口的定义以及改造后 的SayHello代码如下(为了节省空间,将代码合并书写):

using System;

namespace IocInCSharp

{

public interface IHelloGenerator

{

string GetHelloString(string name);

}

public interface ISayHello

{

IHelloGenerator HelloGenerator{ get; set; }

void SayHelloTo(string name);

}

public class SayHello : ISayHello

{

private IHelloGenerator _helloGen;

public IHelloGenerator HelloGenerator

{

get { return _helloGen; }

set { _helloGen = value; }

}

public void SayHelloTo(string name)

{

if(_helloGen != null)

Console.WriteLine(_helloGen.GetHelloString(name));

else

Console.WriteLine("Client.hello is not initialized");

}

}

}





但是我们的MainApp似乎并没有从接口抽象中得到什么好处,从图中看,MainApp居然依赖于三个组件:ICommon.dll、 HelloGenerator.dll以及SayHello.dll。这是由于MainApp在这里负责整体的“装配”工作。如果这三个组件中的任何一个 发生变化,都将导致MainApp.exe的重新编译和部署。从这个角度来看,似乎“针对接口编程”并没有为我们带来太多的好处。

如果能够将“组件装配”工作抽象出来,我们就可以将MainApp的复杂依赖关系加以简化,从而 进一步实现解耦。为此,我们引入“工厂”模式,并利用配置文件和反射技术,动态加载和装配相关组件。

 

(未完,请参考来源)

来源:http://www.cnblogs.com/zhenyulu/zhenyulu/articles/233966.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值