.NET平台依赖注入机制及IoC的设计与实现

我们设计的分层架构,层与层之间应该是松散耦合的。因为是单向单一调用,所以,这里的“松散耦合”实际是指上层类不能具体依赖于下层类,而应该依赖于下层提供的一个接口。这样,上层类不能直接实例化下层中的类,而只持有接口,至于接口所指变量最终究竟是哪一个类,则由依赖注入机制决定。

之所以这样做,是为了实现层与层之间的“可替换”式设计,例如,现在需要换一种方式实现数据访问层,只要这个实现遵循了前面定义的数据访问层接口,业务逻辑层和表示层不需要做任何改动,只需要改一下配置文件系统即可正常运行。另外,基于这种结构的系统,还可以实现并行开发。即不同开发人员可以专注于自己的层次,只有接口被定义好了,开发出来的东西就可以无缝连接。

在J2EE平台上,主要使用Spring框架实现依赖注入。这里,我们将自己做一个依赖注入容器。

依赖注入的理论基础是Abstract Factory设计模式,这里结合具体实例简单介绍一下。

上图以数据访问层为例,展示了Abstract Factory模式的应用。如图,现假设有针对Access和SQLServer两种数据库的数据访问层,它们都实现了数据访问层接口。每个数据访问层有自己的工厂,所有工厂都实现自IDALFactory接口。而客户类(这里就是业务逻辑层类)仅与工厂接口、数据访问层接口耦合,而与具体类无关,这样,只要通过配置文件确定实例化哪个工厂,就可以得到不同的数据访问层。

然而,这种设计虽然可行,但是代码比较冗余,因为这样需要为数据访问层的每一个实现编写一个工厂,业务逻辑层也一样。在以前,我们毫无办法,但是,.NET平台引入的反射机制,给我们提供了一种解决方案。使用反射,每个层只需要一个工厂,然后通过从配置文件中读出程序集的名称,动态加载相应类。另外,为了提高依赖注入机制的效率,这里引入缓存机制。下面来看具体实现。

配置
首先,需要在Web工程的Web.config文件的 <appSettings>节点下添加如下两个项:
<add key="DAL" value=""/>
<add key="BLL" value=""/>
这两个配置选项分别存储要应用的数据访问和也业务逻辑层的程序集名称。value目前是空,是因为目前还没有各个层次的具体实现。

实现缓存操作辅助类
为实现缓存操作,我们将缓存操作封装成一个辅助类,放在Utility工程下,具体代码如下:

using System;
using System.Web;
using System.Web.Caching;

namespace NGuestBook.Utility
{
/**//// <summary>
/// 辅助类,用于缓存操作
/// </summary>
public sealed class CacheAccess
{
/**//// <summary>
/// 将对象加入到缓存中
/// </summary>
/// <param name="cacheKey">缓存键</param>
/// <param name="cacheObject">缓存对象</param>
/// <param name="dependency">缓存依赖项</param>
public static void SaveToCache(string cacheKey,object cacheObject,CacheDependency dependency)
{
Cache cache = HttpRuntime.Cache;
cache.Insert(cacheKey,cacheObject,dependency);
}

/**//// <summary>
/// 从缓存中取得对象,不存在则返回null
/// </summary>
/// <param name="cacheKey">缓存键</param>
/// <returns>获取的缓存对象</returns>
public static object GetFromCache(string cacheKey)
{
Cache cache = HttpRuntime.Cache;

return cache[cacheKey];
}
}
}

封装依赖注入代码
因为很多依赖注入代码非常相似,为了减少重复性代码,我们将可复用的代码先封装在一个类中。具体代码如下(这个类放在Factory工程下):

using System;
using System.Configuration;
using System.Reflection;
using System.Web;
using System.Web.Caching;
using NGuestBook.Utility;

namespace NGuestBook.Factory
{
/**//// <summary>
/// 依赖注入提供者
/// 使用反射机制实现
/// </summary>
public sealed class DependencyInjector
{
/**//// <summary>
/// 取得数据访问层对象
/// 首先检查缓存中是否存在,如果不存在,则利用反射机制返回对象
/// </summary>
/// <param name="className">数据访问类名称</param>
/// <returns>数据访问层对象</returns>
public static object GetDALObject(string className)
{
/**//// <summary>
/// 取得数据访问层名称,首先检查缓存,不存在则到配置文件中读取
/// 缓存依赖项为Web.Config文件
/// </summary>
object dal = CacheAccess.GetFromCache("DAL");
if (dal == null)
{
CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config"));
dal = ConfigurationManager.AppSettings["DAL"];
CacheAccess.SaveToCache("DAL",dal,fileDependency);
}

/**//// <summary>
/// 取得数据访问层对象
/// </summary>
string dalName = (string)dal;
string fullClassName = dalName + "." + className;
object dalObject = CacheAccess.GetFromCache(className);
if (dalObject == null)
{
CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config"));
dalObject = Assembly.Load(dalName).CreateInstance(fullClassName);
CacheAccess.SaveToCache(className,dalObject,fileDependency);
}

return dalObject;
}

/**//// <summary>
/// 取得业务逻辑层对象
/// 首先检查缓存中是否存在,如果不存在,则利用反射机制返回对象
/// </summary>
/// <param name="className">业务逻辑类名称</param>
/// <returns>业务逻辑层对象</returns>
public static object GetBLLObject(string className)
{
/**//// <summary>
/// 取得业务逻辑层名称,首先检查缓存,不存在则到配置文件中读取
/// 缓存依赖项为Web.Config文件
/// </summary>
object bll = CacheAccess.GetFromCache("BLL");
if (bll == null)
{
CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config"));
bll = ConfigurationManager.AppSettings["BLL"];
CacheAccess.SaveToCache("BLL",bll,fileDependency);
}

/**//// <summary>
/// 取得业务逻辑层对象
/// </summary>
string bllName = (string)bll;
string fullClassName = bllName + "." + className;
object bllObject = CacheAccess.GetFromCache(className);
if (bllObject == null)
{
CacheDependency fileDependency = new CacheDependency(HttpContext.Current.Server.MapPath("Web.Config"));
bllObject = Assembly.Load(bllName).CreateInstance(fullClassName);
CacheAccess.SaveToCache(className,bllObject,fileDependency);
}

return bllObject;
}
}
}

实现工厂
下面使用两个辅助类,实现数据访问层工厂和业务逻辑层工厂。

using System;
using NGuestBook.IDAL;

namespace NGuestBook.Factory
{
/**//// <summary>
/// 数据访问层工厂,用于获取相应的数据访问层对象
/// 使用Abstract Factory设计模式+Facace设计模式+反射机制+缓存机制设计
/// </summary>
public sealed class DALFactory
{
/**//// <summary>
/// 获取管理员数据访问层对象
/// </summary>
/// <returns>管理员数据访问层对象</returns>
public static IAdminDAL CreateAdminDAL()
{
return (IAdminDAL)DependencyInjector.GetDALObject("AdminDAL");
}
/**//// <summary>
/// 获取留言数据访问层对象
/// </summary>
/// <returns>留言数据访问层对象</returns>
public static IMessageDAL CreateMessageDAL()
{
return (IMessageDAL)DependencyInjector.GetDALObject("MessageDAL");
}

/**//// <summary>
/// 获取评论数据访问层对象
/// </summary>
/// <returns>评论数据访问层对象</returns>
public static ICommentDAL CreateCommentDAL()
{
return (ICommentDAL)DependencyInjector.GetDALObject("CommentDAL");
}
}
}

using System;
using NGuestBook.IBLL;

namespace NGuestBook.Factory
{
/**//// <summary>
/// 业务逻辑层工厂,用于获取相应的业务逻辑层对象
/// 使用Abstract Factory设计模式+Facace设计模式+反射机制+缓存机制设计
/// </summary>
public sealed class BLLFactory
{
/**//// <summary>
/// 获取管理员业务逻辑层对象
/// </summary>
/// <returns>管理员业务逻辑层对象</returns>
public static IAdminBLL CreateAdminBLL()
{
return (IAdminBLL)DependencyInjector.GetBLLObject("AdminBLL");
}

/**//// <summary>
/// 获取留言业务逻辑层对象
/// </summary>
/// <returns>留言业务逻辑层对象</returns>
public static IMessageBLL CreateMessageBLL()
{
return (IMessageBLL)DependencyInjector.GetBLLObject("MessageBLL");
}

/**//// <summary> /// 获取评论业务逻辑层对象 /// </summary> /// <returns>评论业务逻辑层对象</returns> public static ICommentBLL CreateCommentBLL() { return (ICommentBLL)DependencyInjector.GetBLLObject("CommentBLL"); } }}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


什么是设计模式一套被反复使用、多数人知晓的、经过分类编目的、代码 设计经验 的总结;使用设计模式是为了 可重用 代码、让代码 更容易 被他人理解、保证代码 可靠性;设计模式使代码编制  真正工程化;设计模式使软件工程的 基石脉络, 如同大厦的结构一样;并不直接用来完成代码的编写,而是 描述 在各种不同情况下,要怎么解决问题的一种方案;能使不稳定依赖于相对稳定、具体依赖于相对抽象,避免引
单一职责原则定义(Single Responsibility Principle,SRP)一个对象应该只包含 单一的职责,并且该职责被完整地封装在一个类中。Every  Object should have  a single responsibility, and that responsibility should be entirely encapsulated by t
动态代理和CGLib代理分不清吗,看看这篇文章,写的非常好,强烈推荐。原文截图*************************************************************************************************************************原文文本************
适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户。
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题,总结出来的一套通用的解决方案。
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤。
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容易使用。
单例模式(Singleton Design Pattern)保证一个类只能有一个实例,并提供一个全局访问点。
组合模式可以将对象组合成树形结构来表示“整体-部分”的层次结构,使得客户可以用一致的方式处理个别对象和对象组合。
装饰者模式能够更灵活的,动态的给对象添加其它功能,而不需要修改任何现有的底层代码。
观察者模式(Observer Design Pattern)定义了对象之间的一对多依赖,当对象状态改变的时候,所有依赖者都会自动收到通知。
代理模式为对象提供一个代理,来控制对该对象的访问。代理模式在不改变原始类代码的情况下,通过引入代理类来给原始类附加功能。
工厂模式(Factory Design Pattern)可细分为三种,分别是简单工厂,工厂方法和抽象工厂,它们都是为了更好的创建对象。
状态模式允许对象在内部状态改变时,改变它的行为,对象看起来好像改变了它的类。
命令模式将请求封装为对象,能够支持请求的排队执行、记录日志、撤销等功能。
备忘录模式(Memento Pattern)保存一个对象的某个状态,以便在适当的时候恢复对象。备忘录模式属于行为型模式。 基本介绍 **意图:**在不破坏封装性的前提下,捕获一个对象的内部状态,并在该
顾名思义,责任链模式(Chain of Responsibility Pattern)为请求创建了一个接收者对象的链。这种模式给予请求的类型,对请求的发送者和接收者进行解耦。这种类型的设计模式属于行为
享元模式(Flyweight Pattern)(轻量级)(共享元素)主要用于减少创建对象的数量,以减少内存占用和提高性能。这种类型的设计模式属于结构型模式,它提供了减少对象数量从而改善应用所需的对象结