普通的三层结构:UI / BLL / DAL ,数据实体使用 Model 封装。这种“三层结构”之间是顺序的调用关系,UI 调用 BLL ,BLL 将操作组织并安排 DAL 层,DAL 层操作数据库,每层之间的关系都很紧密,所以协同开发时互相的依赖性较强,项目结构耦合度大。
基于高内聚低耦合的原则,层和层之间的调用考虑引入接口 IDAL 进行规范和分割。BLL 层要求 DAL 层实现的功能先定义好接口 IDAL ,BLL 层就可以借用这些接口去完成业务流程,不必关心实现细节。而 DAL 层只需要按照 IDAL 接口中的定义的操作分别实现,就可以满足 BLL 的要求。这样,使用接口对层和层之间的调用实现分离,设计时都互相不需要了解细节,而在程序运行时,只需要让 BLL 层的接口引用指向对应的实际 DAL 对象,程序自然调用实现好的 DAL 中的功能。
程序通过接口实现了更灵活的分层,但毕竟接口引用哪个 DAL 层对象还是要在 BLL 层进行管理。在数据库表很多,DAL 对象也就会很多的情况下,BLL 层中关于 DAL 的管理也需要很多工作,如何管理更方面?工厂模式。
工厂模式,顾名思义,就是生产某件产品的场所,在程序中起到的也是类似的功能,作用就是生产各式各样的 DAL 对象。这样,BLL 层在需要接口的引用指向对应的 DAL 对象的时候,只需要向工厂要就行了,具体是如何产生的那个对象,BLL 不需要关心。这个框架中,DALFactory 工厂起到的作用就是生产各种 DAL 产品,交给 BLL 层使用,如果需要修改某个 DAL 的应用,只需要在工厂中修改,BLL 依旧还是向工厂要对应的产品即可。
示例代码:
·BLL 层 :
public class UserService
{ private IUserManager ium = DALFactory.CreateUserManager(); }
·DALFactory 层 :
public class DataAccess
{
public static IUserManager CreateUserManager()
{ return new UserManager(); }
}
·DAL 层 :
public class UserManager{ ...... }
这样,工厂在程序当中就起到装配的作用,将 DAL 层的对象装备到 BLL 层中使用。
以上是简单工厂的模型,如果考虑程序的数据库需要在 SQL Server 和 Oracle 之间切换的话,就很有可能需要 SQLDAL 和 OracleDAL 两套数据访问层。因为以前已经引入的接口,各种数据库无非就是不同的实现 IDAL 层的方式而已,对 BLL 层没有影响。如果切换数据库的话,工厂中的各个 Create 方法中需要返回的对象,就必须改成不同的 SQL 和 Oracle 对象。
比如:
return new SQLUserManager(); 或者 return new OracleUserManager();
切换数据库时,工厂中的所有 DAL 对象都需要变成另外一类,数量大的情况下,修改也是相当费力,所以,一般考虑数据库切换一个或多个的情况下,在设计工厂中 Create 方法时,会使用:
public class DataAccess
{
private static string dbName = "SQL";
public static IUserManager CreateUserManager()
{
if(dbName == "SQL") return new SQLUserManager();
if(dbName == "Oracle") return new OracleUserManager();
}
}
通过一个字符串的判断,确定当前需要的 DAL 对象类型,维护时,只需要修改 dbName 就可以实现切换。
这样做的好处显而易见,但问题也同样存在。如果现在需要另一种 Access 数据库的切换,肯定需要再加一条:if(dbName == "Access") return new AccessUserManager(); DAL 对象很多,需要加的就很多,还是很麻烦。这时大家可能发现,各种数据库对应的 UserManager() 的区别就在一个名字上,而且格式很规范,如何根据名字动态产生对应的数据库 DAL 对象呢?反射!
反射的机制允许程序在编译时不知道某些代码的存在,而在运行时动态访问内存中的代码,灵活性可想而知。代码变成:
public class DataAccess
{
private static string path = "UserMIS.SQLDAL";
private static string dbName = "SQL";
public static IUserManager CreateUserManager()
{
return (IUserManager) Assembly.Load(path).CreateInstance(path + "." + dbName + "UserManager");
}
}
所有的判断都是在运行时由反射机制实现的,其灵活性可谓无敌,某天需要加入 Access 数据库操作的 DAL 一系列对象,只需要在设计时实现 IDAL 接口,命名规范。切换数据库时,在 DALFactory 中修改 path 和 dbName 即可。其缺点 1 ,反射的效率低,不过现在的电脑,这点可以忽略;缺点 2 ,易出错,细心一点儿就行了。
以上都是一些简单工厂模式的使用,碰上 DAL 层对象数量巨大,一个使用反射的 DALFactory 也不一定能够轻松维护。怎么办呢?每个 DAL 对象相当于工厂里边一个产品的话,管理起来肯定麻烦,如果把产品整理成 产品线 进行管理,维护时肯定要轻松一些。
public abstract class AbstractFactory //抽象工厂
{
//产生对象的生产线声明
public abstract IUserManager CreateUserManager();
public abstract IStudentManager CreateStudentManager();
//选择对应的工厂
public static AbstractFactory ChooseFactory()
{
string dbName = ConfigurationManager.AppSettings["DBName"];
AbstractFactory factory = null;
if(dbName == "SQL")
factory = new SQLDALFactory();
if(dbName == "Oracle")
factory = new OracleDALFactory();
return factory;
}
}
// SQL 产品线
public class SQLDALFactory : AbstractFactory
{
public override IUserManager CreateUserManager()
{
return new SQLUserManager();
}
public override IStudentManager CreateStudentManager()
{
return new SQLStudentManager();
}
}
// Oracle 产品线
public class OracleDALFactory : AbstractFactory
{
public override IUserManager CreateUserManager()
{
return new OracleUserManager();
}
public override IStudentManager()
{
return new OracleStudentManager();
}
}
定义好产品线和对应的产品,BLL 层取得对应对象时,仍然使用接口的引用指向对应的 DAL 对象
IUserManager ium = AbstractFactory.ChooseFactory().CreateUserManager();
其他照旧。
维护时,切换对象的操作只需要在 AbstractFactory 中切换抽象工厂引用的实体工厂即可。
(转自http://hi.baidu.com/zerozone/blog/item/ef35513dd3a139e63c6d976b.html)