三层开发中容易犯的错误(转)

三层开发中容易犯的错误(转)

我看了这片文章之后,发现自己所做的三层架构还是缺陷多多啊,所以把这片文章转下来……

前记:

相信大家对三层开发都已经耳熟能详,可是我却发现新公司的既有代码中有一些违背分层开发思想的东西,现在与大家分享这些错误,我们共勉之。

如果有人觉得对三层开发拿捏得不是太准,请参照李天平的文章:分层开发思想与小笼包,这篇文章用隐喻说明分层开发,是非常好的一篇文章。

正文:

1.界面层参与非界面逻辑,抢业务逻辑层的饭碗

什么是界面逻辑:<?XML:NAMESPACE PREFIX = O />

界面层应该有的逻辑就是显示的逻辑,例如根据逻辑结果显示某一个Panel不显示另外一个Panel,或者有一个数据集应该在界面上怎么呈现,这是界面层的逻辑

例子场景:

用户登录时首先验证用户输入的用户名是否有效,如果用户名有效,然后再验证用户输入的密码是否和用户名匹配,如果匹配则表示用户可以登录,增加用户的登录次数,然后将用户的信息写入Session中;否则返回错误。在这个过程中除了将用户信息写入Session这一步属于界面逻辑以外其他的操作都应该放在业务逻辑层。

错误代码示例:

ContractedBlock.gif
ExpandedBlockStart.gif
Code

private void buttonLogin_Click(object sender, EventArgs ev)
        {

            
string userName = textBoxUserName.Text;
            
string password = textPassword.Text;

            if (Business.Account.Exists(userName))
            {

                
bool success = Business.Account.Login(userName, password);
                
if (success)
                {

                    Business.Account.AddLoginTime(userName);

                    Session[user= new User(userName, password);
                    Redirect(
/);
                }
                
else
                {

                    
this.labelMessage.Text = 登录失败。;
                }
            }
            
else
            {

                
this.lableMessage.Text = 用户名不存在。;
            }
        }


分析:在上面的代码中一个UI层的一个事件中调用了三次rules层的方法:

Business.Account.Exists(userName)

Business.Account.Login(userName, password)

Business.Account.AddLoginTime(userName);

还附加有条件判断,这种方法在执行效果上面是没有什么错误的,可是却造成了逻辑前移;本来应该在逻辑层执行的判断放在了界面层,是不合适的。

2.数据访问层参与了大量的业务逻辑

这种现象经常出现在大量使用存储过程的系统中,将一大堆逻辑统统放在一个存储过程中实现了,乍一看可能很有效率,其实造成了系统结构的失调,给维护带来困难,数据访问层甚者数据库要抢逻辑层的饭碗了。

还以用户登录为例:

下面是业务逻辑层的登录方法:

ContractedBlock.gif
ExpandedBlockStart.gif
Code

//业务逻辑层的登录方法
        public int Login(string userName, string password) 
        {

            
return DataAccess.UserAccount.Login(userName, password);
        }

下面是数据层的登录方法:

ContractedBlock.gif
ExpandedBlockStart.gif
Code

//数据访问层的登录方法
        public int Login(string userName, string password)
        { 
            SqlParameter[] parameters 
= new SqlParameter[]
            {

                
// ……
            }
            
return SqlHelper.ExecuteProcedure(Login,parameters);
        }

下面是登录的存储过程:

ContractedBlock.gif
ExpandedBlockStart.gif
Code

CREATE PROC Login
            
@userName varchar(20),
            
@password varchar(20)
         
AS 
            
IF NOT EXISTS(SELECT * FROM UserAccount WHERE UserName = @userName)
                
RETURN 1
            
IF NOT EXISTS(SELECT * FROM UserAccount WHERE UserName = @userName AND password = @password)
                
RETURN 1
         
            
UPDATE UserAccount
            
SET LoginTimes = LoginTimes + 1
            
WHERE UserName = @userName
         
            
RETURN 0

分析:从上面三段代码中我们可以很显然得看到登录的业务逻辑已经全部被后移到了数据库的存储过程中。这样使用的三层结构就失去了意义,逻辑层名存实亡了;而数据库的压力会越来越大;我们修改业务逻辑的时候不是到逻辑层修改,而是要到数据库中去修改了。

3.将界面层上的数据组件(如SqlDataSource)作为参数传递到业务逻辑层去赋值

这样做的坏处很明显,本来是界面层依赖于业务逻辑层的,现在业务逻辑层反过来去依赖界面层的类,需要逻辑层引用System.Web命名空间,显然是错误的。

4.为了省事儿,不是直接将参数传递到业务逻辑层,而是通过HttpContext直接获得界面层应该传递的参数

例子:在系统设计的初期没有记录用户登录的IP地址,而到了后期发现了这个问题,要求纪录用户IP了,为了不修改业务逻辑层方法的定义,也不用修改界面层的调用方法,于是便写出了下面的代码:

ContractedBlock.gif
ExpandedBlockStart.gif
Code

public int Login(string userName, string password)
        {

            
string userIP = System.Web.HttpContext.Current.Request.UserHostAddress;
            
//follow is login steps
        }

这一点犯的错误和3中的错误相同,导致层之间形成了依赖环。

5.将事务处理放在数据访问层来做

事务处理应该放在业务逻辑层处理,原因是

1)事务的划分是根据业务逻辑来定义的,通常一个事务就代表完成了一个完整的逻辑操作;

2)一个业务逻辑可能有几个数据操作,修改几个表中的数据,而通常数据层的类的划分是根据数据库表来划分的,如果把事务处理放在数据访问层,那么业务层的方法需要调用两个以上的数据层方法时,就会出现执行两个事务的情况,显然这是不合理的。

总结:

1.在三层结构的划分中我们应该使三层各负其责,谁也不能越权,谁也不能懒惰,通常情况下,逻辑层应该在满足各负其责的条件下,尽可能的厚。

2.三层结构中的依赖关系很明确,界面层依赖于逻辑层,逻辑层依赖于数据访问层,不能互相依赖,而形成依赖环。

转载于:https://www.cnblogs.com/hackerxxw/archive/2008/08/07/1262537.html

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

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/111001.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • REST API和SOAP API之间的区别

    REST API和SOAP API之间的区别TheRepresentationalStateTransfer(REST)架构风格不是可以购买的技术,也不是可以添加到软件开发项目中的库。REST是一种世界观,将信息提升为我们构建的体系结构的第一流元素。RoyFielding博士的论文“架构风格和基于网络的软件架构设计”介绍并整理了用于描述“RESTful”系统的思想和术语。这是一份学术文件,但通过提供RESTful架构的基础,可…

    2022年7月13日
    14
  • linux内核源码下载地址[通俗易懂]

    linux内核源码下载地址[通俗易懂]官网链接:https://www.kernel.org/HTTP https://www.kernel.org/pub/ GIT https://git.kernel.org/ 官网下载经常速度太慢,无法下载,提供另一个链接:http://ftp.sjtu.edu.cn/sites/ftp.kernel.org/pub/linux/kernel/可以根据需要,下…

    2022年7月23日
    9
  • pytest的使用_实例方法只能用实例来调用

    pytest的使用_实例方法只能用实例来调用Pytest执行用例规则Pytest在命令行中支持多种方式来运行和选择测试用例1.对某个目录下所有的用例pytest2.对模块中进行测试pytesttest_mod.py3.对文件夹进行

    2022年8月6日
    3
  • unity3d在工业仿真中的应用_虚拟现实例子

    unity3d在工业仿真中的应用_虚拟现实例子JonathanLinowes(作者),童明(译者)文中示例代码下载亚马逊京东China-Pub当当目录列表内容译者序审校者简介前言第1章万物皆可虚拟11.1虚拟现实对你来说意味着什么21.2头戴式显示器的类型41.2.1桌面VR41.2.2移动VR41.3虚拟现实与增强现实的区别51.4应用与游戏51.5本书涵盖的内容81.6VR体

    2022年9月12日
    0
  • setAttribute的具体用法

    setAttribute的具体用法setAttribute(stringname,stringvalue):增加一个指定名称和值的新属性,或者把一个现有的属性设定为指定的值。1、样式问题setAttribute("cl

    2022年7月1日
    19
  • icmp回复报文_常见的ICMP报文

    icmp回复报文_常见的ICMP报文常见的ICMP报文相应请求我们用的ping操作中就包括了相应请求(类型字段值为8)和应答(类型字段值为0)ICMP报文。过程:一台主机向一个节点发送一个类型字段值为8的ICMP报文,如果途中没有异常(如果没有被路由丢弃,目标不回应ICMP或者传输失败),则目标返回类型字段值为0的ICMP报文,说明这台主机存在。目标不可达,源抑制和超时报文这三种报文的格式是一样的。(1)目标不可到达报文(类型值为3…

    2022年5月1日
    123

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号