浅谈三层架构中的实体类(C#)
最近因为三层架构中的实体类,引发了不少小问题,下面列举一下,谈谈自己的感想。
本文所指的实体类仅限于三层中的实体类,即数据库表的映射。
一、为什么要用实体类?
| 使程序简洁易懂,便于维护。
| 暗合接口不变原则。
| 体现面向对象思想。
举例说明:
不用实体类的三层
假如程序有所变动,需要增加一个参数,学生年龄
用实体类的三层
同样增加一个参数,学生年龄
很明显的看出,用实体类之后,代码明显变得简洁,面向对象封装思想。
最重要的是,如果将来有所改动,只需要改动实体类,方法间调用接口,完全不需要变动,大大减少了程序修改量,迎合了面向对象中接口不变的思想。
甚至在程序设计时,就把将来可能需要的属性预先放在实体类中,这样以后变动时,连实体类都不用变动。
二、实体类缺点在哪(仅是个人观点)?
虽然实体类有明显的优势,但亲身使用时会发现一个明显的问题:代码可读性貌似降低了。
上边例子中体现的不是很明显。再来一个简单的例子。
假如有一个方法(没有用实体类):
selectByPartDate(DateTime startDate,DateTime endDate){}
此方法用来查询某段时间内的记录,两个参数:一个是起始时间,一个是结束时间。
虽然没有用实体类,可是让人看起来很舒服,一看就知道要干什么。
用了实体类后(假设实体类名是LoginLog):
selectByPartDate(LoginLog startDate, LoginLog endDate){}
让人看了不知所措,难以理解到底想做什么。
当然,这种不知所措可以通过两个途径解决:
| 良好的命名规范,达到 “见名知意”。
| 良好的文档说明或者代码注释。
在这,引出一个问题:三层中,所有的方法都要用实体类传递参数吗?
最明显的就是上边selectByPartDate例子,还有诸如:deleteByID(longid){}这样的方法。真的有必要传递实体类吗?
实体类的确是容易扩展、修改,可这不违反设计原则吗?
设计原则是:尽量避免对原有代码的修改,而是通过增加代码的方式去解决。
诸如selectByPartDate、deleteByID这样的方法,目的已经很明确了:查询某段时间内的记录、根据id删除记录。换句话说,这就是它们存在的意义。
程序再怎么扩展,能涉及到这类方法?假如真的把这类方法扩展了,那么它们完全失去了自己的意义!
deleteByID就是根据id删除,扩展了,就不是根据id删除了!这时候你可以再添加一个新的方法,何必和deleteByID过不去呢?
对于参数极少,目的明确的方法,个人认为没必要用实体类。
这只是我的个人看法,欢迎高人指点!
三、源自实体类的灵感!
三层中,大家都知道U层的逻辑应该尽量少,尽可能的转移到B层。
但是实际应用中如何转移却是个问题。
就拿删除记录来说,U层调用B层,B层调用D层,由D层返回布尔值,来说明删除是否成功。
这样传递参数,最终U层得到的是一个布尔值,真或假,携带的信息太少了,只能反馈给用户成功或者失败(同时多了一次判断逻辑),而为什么失败,就无从得知了。
现实应用中有很多这样的例子,有时候为了给出用户更详细的提示,不惜把B层分的很细,然后在界面大量的调用B层方法,加上大量的判断,才能给用户一个完整的提示。
这样的情形显然不是我们想要的?那么怎么解决呢?
造成这种情况的根本原因就是布尔型返回值携带的信息太少,哪个类型多?当然是字符串型!
在D层,可以返回布尔值,让B层判断使用。但是B层返回给U层的值,强烈反对是布尔型的!
按照这个思路,在B层使用复杂的逻辑,然后把执行结果以字符串的形式的返回。这样一来,U层无需任何判断,直接把B层返回的字符串显示出来就行了!
举个例子(B层的一个方法):
public string deleteMail(string domainName, Mail mail) { try { //删除POP3服务器中的邮箱 CMDHelper cmdHelper = new CMDHelper(); string cmdString = ""; cmdString = cmdHelper.execute("DEL " + mail.mailName + "@" + domainName + " /DELETEUSER"); if (cmdString.ToLower().IndexOf("successfully") >= 0) //删除成功 { //同步数据库 IMailDAO mailDAO = creater.createMailDAO(); if (mailDAO.deleteByID(mail)) { return "删除成功!数据库同步成功!"; } else { return "删除成功!但数据库同步失败!"; } } else if (cmdString.IndexOf("系统找不到指定的文件") >= 0) //不存在 { return "该邮箱不存在!"; } else { return "未知原因错误!请联系管理员!"; } } catch (Exception ex) { throw ex; } }
很明显的看出,一切判断都是在B层完成的,然后把结果以字符串形式返回给U层,简洁明了,U层不需要任何逻辑,直接show就行了!
这样看上去很好,但是字符串还是比较让人不舒服,既然是面向对象,为何不返回一个实体类呢?
我们可以定义一个实体类,名字就叫LayerParameter。给这个实体类加一个字符串型的resultString属性,就把刚刚的字符串返回值封装进来了。就用这个实体类作为B层给U层的返回值。
这样做简直完美!
有经验的朋友可能遇到过刷新界面的问题,也就是U层需要根据实际情况来刷新界面数据,在B/S结构中尤其明显。有了实体类做返回值,就啥都不怕了!不就是刷新吗?在LayerParameter实体类中加一个布尔型属性refresh,U层调用B层后,show一下返回值(LayerParameter类实例)的resultString属性,把执行信息告诉用户,然后再判断一下refresh属性,决定是否刷新界面数据(U层一点逻辑都没有是不可能的!),此时B层给U层的返回值,仍然是LayerParameter,接口无需任何改动。
以上仅仅是个人想法,希望大牛指点!
最后,申明一点:
一切的一切还是要以项目实践为基础,经验才是王道,否则一切讨论都是空穴来风!