本章内容主要包括两个方面,一、是框架分层(控制器、业务对象、实体、Dao)的详细说明,二、是对比常用三层结构的区别和优势;
本文要点:
1.框架中的各个分层详细说明
2.对比常用三层结构的区别和优势
3.分享两个项目中的小经验
4.网络资料
我们先看一下前面实例中的解决方案目录:
我们再看各层之间的调用关系:
上图描叙的控制器有四种方式来操作数据库,
1)控制器调用实体,通过框架中的ORM来实现单表的操作
2)控制器直接操作数据库对象(oleDB),通过编写SQL语句访问数据库
3)控制器通过调用Dao操作数据库
4)控制器调用业务对象,业务对象再调用Dao操作数据库
还有就是每一层在程序结构中承担的角色;
一、Dao
数据访问对象,新建一个Dao必须继承继承框架中的AbstractDao对象,AbstractDao对象中封装了数据库操作对象oleDb,所以Dao中的方法都是直接编写SQL语句操作数据库;
public class BookDao : EFWCoreLib.CoreFrame.BusinessArchitecture.AbstractDao
{
public DataTable GetBooks(string searchChar, int flag)
{
string strsql = @"SELECT * FROM dbo.Books WHERE BookName LIKE '%{0}%' AND Flag={1}";
strsql = string.Format(strsql, searchChar, flag);
return oleDb.GetDataTable(strsql);
}
}
上面代码是实现对Sqlserver数据库的操作,如果要实现不同数据库的操作,如:Oracle、DB2等,要如何实现?很简单,先创建IBookDao接口,再分别实现SqlBookDao和OracleBookDao,控制器访问Dao只需调用IBookDao接口就行了,而最后操作哪个数据库的Dao在EFWUnity.config配置文件中配置好。
IBookDao接口
public interface IBookDao
{
System.Data.DataTable GetBooks(string searchChar, int flag);
}
SqlBookDao对象
public class SqlBookDao : EFWCoreLib.CoreFrame.BusinessArchitecture.AbstractDao, Books.Dao.IBookDao
{
public DataTable GetBooks(string searchChar, int flag)
{
string strsql = @"SELECT * FROM dbo.Books WHERE BookName LIKE '%{0}%' AND Flag={1}";
strsql = string.Format(strsql, searchChar, flag);
return oleDb.GetDataTable(strsql);
}
}
OracleBookDao对象
public class OracleBookDao : EFWCoreLib.CoreFrame.BusinessArchitecture.AbstractDao, Books.Dao.IBookDao
{
public DataTable GetBooks(string searchChar, int flag)
{
string strsql = @"SELECT * FROM Books WHERE BookName LIKE '%{0}%' AND Flag={1}";
strsql = string.Format(strsql, searchChar, flag);
return oleDb.GetDataTable(strsql);
}
}
EFWUnity.config配置文件
控制器中通过NewDao<>操作方法,创建配置的SqlBookDao对象
综合上述,不同数据库Dao结构设计如下:
二、Entity实体
实体代码都是用工具,根据数据库表结构信息生成的;所有实体都必须继承框架中的AbstractEntity对象,而实体与数据库表之间的映射不需要另外xml配置文件,而是利用自定义标签实现ORM映射;TableAttribute标签映射实体类名与数据库表,ColumnAttribute标签映射实体属性与数据库表字段。一个实体映射到多个表也可以,直接类名上配置多个TableAttribute标签和属性上配置多个ColumnAttribute标签,不过要用标签的Alias参数区分。
三、ObjectModel业务对象
业务对象在书籍管理实例并没有建此对象,因为业务太简单了,只有当业务流程很复杂或范围涉及广的情况,就可以考虑建一些业务对象来解决这些问题;所有业务对象必须继承框架中的AbstractBusines对象,业务对象中不能直接操作数据库,必须通过Dao来访问。另外就是如果多个业务对象是用来解决同一个问题,一般我们使用工厂模式来设计,在框架中可以使用EFWUnity.config配置文件进行业务对象映射;
四、Controller控制器,包括WebController、WinController、WcfController、WcfClientController
控制器分为3种模式,不同模式分别建控制器。控制器在程序结构中的作用就是承上启下,比如WebController控制器,就是把调用逻辑层返回的数据结构转换为统一Json字符串传递给界面JqueryEasyUI。而不同的界面框架则需要对框架中的Webcontroller进行扩展,以后我们会详细讲解各种控制器的实现;
还有就是Controller中提供了数据库的操作对象OleDB,允许控制器直接编写SQL语句操作数据库,为什么要放开此功能,这也是在项目中遇到的实际情况而不得不妥协的一种设计;
同样所有控制器也都必须框架中的基类控制器,基于JqueryEasyUI的Webcontroller继承AbstractJqueryController对象,WinController继承BaseController对象,wcfController继承JsonWCFController对象;wcfClientController继承BaseWCFClientController对象;
再就是控制器暴露给界面UI,还必须加上控制器的自定义标签,WebController的WebController和WebMethod,WinController的Menu,wcfClientController的WCFController和WCFMethod;
通过上面的介绍,应该对EnterpriseFrameWork框架的分层结构有一定得了解,下面我们讨论一下,与常用三层结构的区别与优势?
软件分层意义主要就包括解耦和复用,也许还能让代码维护与扩展更方便;三层结构分别包括界面层、逻辑层和数据层。EnterpriseFrameWork框架中的分层也可以说是三层,只是叫法与承担的职责不一样,并且EnterpriseFrameWork框架中的分层可以随时变化的,不同情况有4种方式对分层进行调整,所以EnterpriseFrameWork框架分层的兼容性强。有哪些不同情况,可以总结为两种,一是根据具体功能的难易程度,二是根据开发人员的代码水平;
我们对比一下PetShop项目的程序结构:
PetShop项目名称及描述:(实现步骤为:4-3-6-5-2-1)
1、WEB=表示层
2、BLL=业务逻辑层
3、IDAL=数据访问层接口定义
4、Model=业务实体
5、DALFactory=数据层的抽象工厂(创建反射)
6、SQLServerDAL=SQLServer数据访问层 / OracleDAL=Oracle数据访问层 DBUtility 数据库访问组件基础类
其中3中的IDAL对应EnterpriseFrameWork框架中的Dao接口,Model对应框架中的实体,SQLServerDAL和OracleDAL对应不同数据库Dao;而不同的有5中的DALFactory在框架中是不需要的,只需配置文件中配置即可;2中的BLL也职责不一样,框架中更趋向领域模型的设计;1中的WEB表示层,框架中单独把Controller控制器分出来独立一层;所以对比两者发现其思想还是差别比较大的,个人觉得EnterpriseFrameWork框架除了在程序代码的解耦,更在业务、项目等实际情况方面更加合适;
接着再讨论一下:贫血模型与充血模型?
贫血模型:是指领域对象里只有get和set方法,或者包含少量的CRUD方法,所有的业务逻辑都不包含在内而是放在Business Logic层。
充血模型:层次结构和上面的差不多,不过大多业务逻辑和持久化放在Domain Object里面,Business Logic(业务逻辑层)只是简单封装部分业务逻辑以及控制事务、权限等。
对这两种模型的使用个人感觉比较深刻,以前还不知道这种写法是贫血模型,表生成所有实体,再就是调用实体的N个逻辑对象,这样的代码就越写越过程化,基本上一个系统分成几个逻辑对象就完成了,没有了对象的概念;后来就觉得这样不对,就把实体扩展成一个完整的业务对象,实现对这个实体的所有业务操作方法;这样刚开始还蛮有感觉,但后来越做越别扭,一是很多实体根本没有业务操作,还有就是一个业务操作会跨多个实体;在实体上来补充业务操作方法本来就不太合理,因为表结构的设计本来就不是基于面向对象的;所以后来就演变为现在EnterpriseFrameWork框架中ObjectModel这种模型,我叫做领域对象模型;简单的业务走第一种模式,复杂的业务就必须分析出领域模型并设计业务对象;
最后分享两个项目中的小经验,一个是与大学高校合作开发项目、另一个是小公司老板的见解;
与大学高校合作开发一个不算太复杂的项目,公司方负责需求的收集与分析,高校针对需求进行设计与开发;高校由一个研究生导师带队,也没用什么技术框架,就是常用的三层结构,合作的过程就不讲了,反正就是最后到我接手维护代码的时候就感觉到这个系统的坑人之处;首先不算复杂的系统项目建了十多个,找代码文件很难找,再就是每改一个小地方要改多个文件从界面层改到数据层;最受不了的就是代码的阅读与调试相当麻烦,每层之间又通过一个工厂反射类名方法名调用;最后我们得出的结论就是在实际项目千万不能交给高校开发,他们可以搞一些研究项目,做的时候又没有考虑到一些实际情况;
小公司老板的见解。一朋友自己做老板创业,懂点php网页开发,在学校招了几个刚毕业生准备研发一个新产品,要我帮忙他们搭建系统框架,我推荐最早框架给他们使用,那时候在Controller中并没有OleDB操作数据库;我先把系统的核心业务开发完成,招的几个毕业生就在此基础上继续完善系统,刚开始我在的时候还好,有什么不明白的我帮忙解决,后来我抽出来后,他们做着做着问题就暴露出来了,一个简单的功能很久都搞不出来,新产品讲究的就是速度嘛,马上要给客户看的;所以朋友急了开始找原因,虽然朋友就会个php网页制作,但通过与几个毕业生深入沟通,他发现程序分层太复杂了,做一个功能要画界面,写控制器、写业务对象、写Dao才能完成,更难的他们根本对逻辑层的控制器、业务对象、Dao理解不了,所以一定要他们这样用就经常搞出一些莫名其妙的问题;朋友就说为什么不能直接在控制器中写sql语句,这样开发起来多简单?我就说这样放开不行,会破坏整个程序的分层结构,以后代码的维护、扩展都会有问题,但我的这些以后说服不了他,他觉得这些以后版本可以优化,现在就是要马上出来;最后只要在控制器也开放了Oledb操作数据库;
网络上对于三层结构的解释:
数据访问层:有时候也称为是持久层,其功能主要是负责数据库的访问。简单的说法就是实现对数据表的Select,Insert,Update,Delete的操作。如果要加入ORM的元素,那么就会包括对象和数据表之间的mapping,以及对象实体的持久化。在PetShop的数据访问层中,并没有使用ORM,从而导致了代码量的增加,可以看作是整个设计实现中的一大败笔。
业务逻辑层:是整个系统的核心,它与这个系统的业务(领域)有关。以PetShop为例,业务逻辑层的相关设计,均和网上宠物店特有的逻辑相关,例如查询宠物,下订单,添加宠物到购物车等等。如果涉及到数据库的访问,则调用数据访问层。
表示层:是系统的UI部分,负责使用者与整个系统的交互。在这一层中,理想的状态是不应包括系统的业务逻辑。表示层中的逻辑代码,仅与界面元素有关。在PetShop中,是利用ASP.Net来设计的,因此包含了许多Web控件和相关逻辑。
分层的好处
1、开发人员可以只关注整个结构中的其中某一层;
2、可以很容易的用新的实现来替换原有层次的实现;
3、可以降低层与层之间的依赖;
4、有利于标准化;
5、利于各层逻辑的复用。
概括来说,分层式设计可以达至如下目的:分散关注、松散耦合、逻辑复用、标准定义。
分层的坏处:
1、降低了系统的性能。这是不言而喻的。如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成。
2、有时会导致级联的修改。这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码。