【机房重构】一步一步往上爬——不仅仅是三层

	不知道大家还记不记得之前学习的UML中一个单独列出来的一种图,也就是这次我想说的包图。那个时候,让我们画机房收费系统的各种图,用例图、类图等等,通过自己反复琢磨,还都勉勉强强画出来了。唯独只有包图,我是一点东西也没有画上,只是见到了传说中的“包”是长什么样子的。
	现在到了机房重构的阶段了,之前也学习了三层,终于是知道了包图该怎么画了。但也不仅仅是理解了包图,而且也是见证到了包图是如何在一步一步层层升级的。下面就将用一个机房系统中的登录实例来看看为什么说《不仅仅是三层》。
	三层的学习在有了一个初识之后,欠缺的就是一个实践了。每一层与每一层的关系理清了,登录小实例也就很容易实现了。下面先看看仅仅是三层的一个登录包图:

	从图中可以很清楚的看到三个层之间的关系。用个形象的比喻去说,就是一场擂台赛,双方代表分别为U层和D层,其中裁判由B层担任。
	言归正传,下面描述一下这一登录实例中的一个时序:用户输入信息,通过实体将信息从U层传入B层,而另一过程,通过实体将信息从D层也传入B层。于是,开始在B层进行逻辑判断的过程,最后同样是通过实体将B层结果返回给U层,以回馈给用户。但这样的话,并不能很好地符合”低耦合“的编程思想,所以三层+Model(实体层)需要升级了。
	如何升级?这里,之前学习的设计模式就可以应用上了。外观模式与抽象工厂模式,在那段时间还不能很好地理解它们的作用,而现在机房重构了,对于它们的作用真的是有了一个不一样的深刻的体会:为了降低U层与B层之间的耦合,我们在两者之间加入了外观模式;为了降低D层与B层之间的耦合,我们在两者之间加入了工厂模式和接口层。
	下面就是一个全新的包图:

	看着这样一个图,自己是一次又一次的琢磨才把关系给理清楚的呀。理论上看起来确实很容易就理解了,但对于代码的编写,真的是不容易了。参数的传来传去,时不时的就会把自己给传进去出不来了。所以说,在自己多次糊涂与清醒之后,感觉最重要的还是要明确每一层应该做的,在此先用最简洁的语句来说说究竟每层起个什么样的作用:
	1.UI(用户界面层),一方面是将用户输入的信息传入给外观层,另一方面是将外观层的信息显示给用户;
	2.Facade(外观层),一方面是接收U层用户的信息传给B层进行后续操作,另一方面是接收B层的信息传给U层;
	3.BLL(逻辑判断层),一方面是接收外观层传来的信息,另一方面是接收通过工厂和接口后传来的D层的信息;
	4.Factory(工厂层),其中通过读取配置文件,在以后更换数据库的时候,只需要增加一个新的类,而不需要修改原来的代码;
	5.IDAL(接口层),其中就是对数据库进行增、删、改、查操作的封装,降低B层与D层的直接耦合;
	6.DAL(数据访问层),是通过实现接口的方法而去对用户的需求做相应地操作,一方面可能是需要查询数据库中的数据,另一方面则可能是为了增删改数据。而为了减少代码量,又用到了一个SQLHelper类。
	7.Model(实体层),数据的传输,既然需要避免传输者与接收者之间的耦合,那么总得需要一个中介,实体层便是那个中介,对封装好后的数据进行传输。
	在知道了每一层的作用,下面就是每一层该怎么去做。通过前些天的琢磨,一次次的单步调试,总算是对这个《不仅仅是三层》的登录实例有了进一步的认识。下面就通过一张时序图再次理理究竟各层之间是一个怎么样的关系才实现登录的:

	这里就不再描述了,图中画得肯定比我写下来的清楚的多。如果大家有什么意见或建议,也欢迎来与我一起交流交流。下面看看各个层中一些重要的代码:

SQLHelper类(这里用到的是有参数的查询操作):

 Public Function ExecSelect(ByVal cmdText As String, ByVal cmdType As CommandType, ByVal paras As SqlParameter()) As DataTable

        Dim sqlAdapter As SqlDataAdapter
        Dim dt As New DataTable
        Dim ds As New DataSet
        '还是给cmd赋值
        cmd.CommandText = cmdText
        cmd.CommandType = cmdType
        cmd.Connection = conn
        cmd.Parameters.AddRange(paras)  '参数添加
        sqlAdapter = New SqlDataAdapter(cmd)  '实例化adapter
        Try
            sqlAdapter.Fill(ds)           '用adapter将dataSet填充
            dt = ds.Tables(0)             'datatable为dataSet的第一个表
            cmd.Parameters.Clear()        '清除参数
        Catch ex As Exception
            MsgBox("查询失败", CType(vbOKOnly + MsgBoxStyle.Exclamation, MsgBoxStyle), "警告")
        Finally                            '最后一定要销毁cmd
            Call CloseCmd(cmd)
        End Try
        Return dt

    End Function

BLL层:

Public Class LoginBLL
    '检查用户是否存在
    Public Function IsUserExists(ByVal UserInfo As Model.EntityUserInfo) As Boolean

        Dim factory As New Factory.LoginFactory()
        Dim Iuser As IDAL.IUserinfo

        '调用"创建用户"的工厂方法
        Iuser = factory.CreateUserInfo()
        Dim table As DataTable
        Dim flag As Boolean
        table = Iuser.QueryUser(UserInfo)
        '由于在sqlHelper中返回的形式为表格形式(adataset.Tables(0)),且开头第一列表示为0,所以Item(0)则代表用户名
        If table.Rows.Count = 0 Then
            flag = False
        Else
            flag = True
        End If
        Return flag

    End Function
    '查看密码是否正确
    Public Function isPWDright(ByVal UserInfo As Model.EntityUserInfo) As DataTable
        Dim factory As New Factory.LoginFactory()
        Dim Iuser As IDAL.IUserinfo
        Dim table As DataTable   '中间变量,用于存储D层查询到的数据
        Iuser = factory.CreateUserInfo   '调用工厂的CreateUserInfo方法创建Iuser接口实例
        table = Iuser.QueryUser(UserInfo)  '调用接口的方法.
        Return table
    End Function
End Class

DAL层:

Public Class LoginDAL : Implements IDAL.IUserinfo
    '实现接口中的方法。
    Private sqlHelper As SQLHelper.SqlHelper = New SQLHelper.SqlHelper
    Public Function QueryUser(ByVal UserInfo As Model.EntityUserInfo) As DataTable Implements IUserinfo.QueryUser

        Dim Sql As String        <span style="font-family: Arial, Helvetica, sans-serif;">'中间变量,用于储存从数据库中查找到的信息</span>
        Dim table As DataTable    '声明一个DataTable类型变量
        Dim sqlParams As SqlParameter() = {New SqlParameter("@UserName", UserInfo.ID), New SqlParameter("@password", UserInfo.Password)} '声明并实例化参数数组
        Sql = "select * from User_Info where UserID=@UserName and UserPassword=@password"
        '下句为调用SqlHelper类中的GetDataTable()方法来执行查询,并获取返回值
        table = sqlHelper.ExecSelect(Sql, CommandType.Text, sqlParams)
        Return table
    End Function

End Class

Facade层:

Public Class LoginFacade
    Public Function CheckUser(ByVal UserInfo As Model.EntityUserInfo) As Boolean
        '用于检查用户是否存在
        Dim IsUserExists As New BLL.LoginBLL()
        Dim flag As Boolean
        flag = IsUserExists.IsUserExists(UserInfo)
        If flag = True Then
            Return True
        Else
            Return False
        End If
    End Function
    '检查密码是否正确
    Public Function CheckPwd(ByVal UserInfo As Model.EntityUserInfo) As DataTable
        Dim IsPwd As New BLL.LoginBLL()
        Dim table As DataTable
        table = IsPwd.isPWDright(UserInfo)
        Return table
    End Function
End Class

Factory层:

Public Class LoginFactory
    Dim strDB As String = System.Configuration.ConfigurationSettings.AppSettings("DBString")
    Private Shared ReadOnly AssemblyName As String = "DAL" '数据程序集名称
    Public Function CreateUserInfo() As IDAL.IUserinfo
        'CType是一个内联函数,将前部分的表达式转换为后面的类型
        Return CType(Assembly.Load("DAL").CreateInstance("DAL" & "." & "LoginDAL"), IDAL.IUserinfo)     '返回Iuserinfo
    End Function
End Class

UI层:

Public Class frmLogin
    Private Sub btnOK_Click(sender As Object, e As EventArgs) Handles btnOK.Click
        If txtUserName.Text = "" Then
            MessageBox.Show("请输入用户名!", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            txtUserName.Text = ""
            txtUserName.Focus()
            Exit Sub
        ElseIf txtPassword.Text = "" Then
            MessageBox.Show("请输入密码!", "", MessageBoxButtons.OK, MessageBoxIcon.Exclamation)
            txtPassword.Text = ""
            txtPassword.Focus()
            Exit Sub
        End If

        '定义一个外观层的对象
        Dim FacadeLogin As New Facade.LoginFacade
        Dim UserInfo As New Model.EntityUserInfo
        UserInfo.ID = txtUserName.Text
        UserInfo.Password = txtPassword.Text
        Dim strResult1 As Boolean
        strResult1 = FacadeLogin.CheckUser(UserInfo) '将U层的文本框的内容传入外观层,然后通过外观层传入B层进行判断
        If strResult1 = False Then
            MsgBox("用户不存在或者密码不正确!")
            txtUserName.Text = ""
            txtPassword.Text = ""
            txtUserName.Select()
            txtUserName.Focus()
        End If
        Dim table As DataTable
        table = FacadeLogin.CheckPwd(UserInfo)
        If Trim(txtPassword.Text) = Trim(table.Rows(0).Item(3)) Then
            MsgBox("登陆成功!")
            Me.Hide()
            MDIfrmMain.Show()
            txtUserName.Text = ""
            txtPassword.Text = ""
        End If

    End Sub
End Class

学习心得:

	其实,在一次次重复之后,对于这个实例也就不再那么迷惑了。但从画图的那一个过程来看,自己还是很吃力的,自己还是没有完全理解,但最起码比刚开始要好很多,通过后面的功能的实现,自己也是会越来越熟悉的,到时候,理解得肯定也就更加深刻了。
	机房重构,没有捷径,只有一步一步自己往上爬,蜗牛精神,继续保持下去~~
时间: 2024-10-26 04:53:57

【机房重构】一步一步往上爬——不仅仅是三层的相关文章

【机房重构】一步一步往上爬——又见UML与文档

机房重构的代码编写完成后,下一阶段的任务就是画图和文档了.在师父细心的一番指导后,开始动工了. 在数据库设计的时候,自己也有根据自考学习的知识,画了张ER图.到现在系统完成后,回头看之前贴在博客上的那张ER图,也是错漏百出,自己都不知道误导了多少人了.. 今天一开始把ER图给师父看,师父第一个问题就是问ER图是给谁看的?我顿时懵了.师父接着说ER图是给用户看的,你用英文写,用户看的懂吗? 下面就从ER图说起: ER图,是需要在需求分析文档中出现的,那么就是需要给用户看到的,而不是程序员或者说是开

【机房重构】一步一步往上爬——验收给了我什么

整个机房重构过程中,一共经历了两次验收.一次是关于系统编程:一次是关于画图与文档,每一次,师父都是耐心.细心地指导.要问验收给了我什么,看下面的博客内容便清楚了. 个人机房重构可分为两个阶段,前期主要是代码的编写,后期主要是画图. 在前期阶段,慕夏师父也是非常关心我的进展,时常来我这里看看,问问我有什么问题,抓住这几次机会,师父也是和我说了很多,从而对整个机房收费系统的业务逻辑更加清楚了.刚开始接触到抽象工厂+配置文件+反射的时候,除了茫然还是茫然,几天下来没有什么成果,登录的主线也没有完全明白

【机房重构】一步一步往上爬——七层中的那些事

机房重构开始已经一个多星期了,从最开始的理解登录到现在已经成功完成至少一次的"增"."删"."改"."查"的操作,现在在七层的这个大环境下,从最开始的奄奄一息,终于变得生龙活虎起来了. 之前总是听师哥师姐们说,敲完登录一条线了,后面就会很顺利了.可是,从我来说,事实并非如此.然而,磕磕绊绊,四个字足以形容我的这些天.不过,我心态好,我可以忍受一个人花时间调代码的孤独,再说,我也可以找小伙伴.找师父帮助我,我有什么理由不成功.

【机房重构】一步一步往上爬——数据库设计

期末考试结束了,寒假全职生活如期而至,终于可以开始全身心的投入我的机房重构了.又是一个新的项目,万事开头难,但不开头更难.自己也只能是一步一步往上爬,机房重构便从数据库设计开始. 回想去年的自考学习,<数据库系统原理>中的知识就可以在机房重构的时候好好应用一把了.第二章的<数据库设计和ER模型>很仔细地教了我们如何进行数据库设计.所以,在参考自考书的基础上,把重构时的数据库全新地设计了一番. 首先明确数据库系统的生存期一般可划分为七个阶段:规划.需求分析.概念设计.逻辑设计.物理设

【机房重构】一步一步往上爬——小问题大收获

机房重构进行了半个月之久了,其中遇到了不少问题,有的调试了很久,有的是因为自己的大意.不管怎样,自己还是通过各种问题,收获了许多,成长了许多.这篇博客主要就是对之前遇到的一些问题的集锦,希望能给大家一些帮助,遇到问题,成功地解决问题. 问题一:缺少参数,在U层传参数的时候少写了一个参数. 解决:根据提示检查U层的该参数,将该参数赋上相应的值. 问题二:进程无法访问文件,文件正由另一个进程使用. 解决:重新生成解决方案,或者是关闭程序,重新启动程序. 问题三:接口的实现错误,类型无法转换,这是因为

【机房重构】一步一步往上爬——企业管理器,好好利用

这么些天,一直在机房重构中.这么些天,从刚开始的迷茫,到已经完全理解七层间的调用,现在是敲代码敲得恶心了.因为自己好像什么都没有做,只是在一遍又一遍的敲重复的代码,有时候,真的不想干了.但不干是不行的,所以,还是想找些其他方法,在挥去一些厌烦情绪的同时,自己也学习些新的东西. 因为太多重复的代码,所以导致了自己不想敲下去,在经过了一些新的尝试后,总算是有种柳暗花明的感觉.那么,本篇博客就将介绍一下在机房收费系统中数据库中的企业管理器的应用,它的强大,以前只是听说或者看过,但自己还没有尝试过.这一

一步一步写算法(之克鲁斯卡尔算法 上)

原文:一步一步写算法(之克鲁斯卡尔算法 上) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     克鲁斯卡尔算法是计算最小生成树的一种算法.和prim算法(上,中,下)按照节点进行查找的方法不一样,克鲁斯卡尔算法是按照具体的线段进行的.现在我们假设一个图有m个节点,n条边.首先,我们需要把m个节点看成m个独立的生成树,并且把n条边按照从小到大的数据进行排列.在n条边中,我们依次取出其中的每一条边,如果发现边的两个节点分别位于两棵树上,

一步一步写算法(之prim算法 上)

原文:一步一步写算法(之prim算法 上) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     前面我们讨论了图的创建.添加.删除和保存等问题.今天我们将继续讨论图的一些其他问题,比如说如何在图的环境下构建最小生成树.为什么要构建最小生成树呢?其实原理很简单.打个比方,现在某一个乡镇有n个村,那么这n个村肯定是联通的.现在我们打算在各个村之间搭建网线,实现村村通的工程.那么有什么办法可以实现村村互通,同时又使得最后的总距离最小呢?要达

一步一步写算法(之哈夫曼树 上)

原文:一步一步写算法(之哈夫曼树 上) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]       在数据传输的过程当中,我们总是希望用尽可能少的带宽传输更多的数据,哈夫曼就是其中的一种较少带宽传输的方法.哈夫曼的基本思想不复杂,那就是对于出现频率高的数据用短字节表示,对于频率比较低得数据用长字节表示.     比如说,现在有4个数据需要传输,分别为A.B.C.D,所以一般来说,如果此时没有考虑四个数据出现的概率,那么我们完全可以这么分配