浅谈三层架构中的实体类(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,接口无需任何改动。

         以上仅仅是个人想法,希望大牛指点!

 

         最后,申明一点:

         一切的一切还是要以项目实践为基础,经验才是王道,否则一切讨论都是空穴来风!

posted @ 2012-08-18 15:52  杨元  阅读(1748)  评论(0编辑  收藏  举报