合作版的机房收费系统紧锣密鼓的进行着,从一开始的讨论到角色分配再到如今系统框架设计完成,这期间很顺利,唯有一点是系统需求的变更。以前收费系统的设计没有临时用户的功能,但当系统框架设计到一半时又新增加了功能,系统能够允许临时用户上机,但是上机的收费方式不同。需求的变更,使得本来很顺利的开发过程,收到了小的冲击,因为添加临时用户后相应的很多功能都需要重新定义,包括系统数据库也需要重新进行一些变更来适应需求的变更。
为了适应需求的变更,我们在数据库中又新增加了临时用户的表结构,虽然不是最好的解决办法,但是相比修改其它的表结构也快捷的多。另外在设计业务逻辑时,在结账上也新增加了临时用户这一功能,在面向对象系统设计原则中要符合开放-封闭原则,对修改封闭,扩展开放,所以这时不得不采用一种设计模式——建造者模式来实现结账功能,因为结账的这几部工作内部过程大致相同,但是显示不同,所以这时建造者模式就很能适应系统需求的变更。
建造者模式将一个复杂对象的构件与他的表示进行了分离,通俗点说其实是将结果和过程进行了分离,在传统的软件编写过程中,为了实现某一功能我们常常定义某一函数,在这个函数中编写代码来实现这一功能,这种方法看起来简单,但是却失去了可重构性,如果系统需求变更,我们就要打动干戈彻底的对函数修改一番,这样就违背了开放-封闭原则,但是在系统中往往表示和实现是相分离的,为了实现这个目的我们引入了建造者模式。
建造者模式将一个复杂对象的构建与表示进行了分离,使得同样的过程创建出了不同的表示。如下图:
长的圆角矩形图形1为具体的构件方法类,图2和图3分别指定了构件的图形圆形和正方形,经过图形1的构件后分别构件成了两种图形,这种构件方法也就是建造者模式,它将构件方法和具体的构件结果分离,在经过了相同的过程后得到了不同的结果。这样将表示和过程进行分离,能够增强系统的可扩展性,如果我们需要构件其它图形,只需指定需要构件的对象,新增一个构件对象即可。
建造者模式的具体结构图:
机房收费系统的结账功能要求我们选择用户名后会在选项卡中生成该用户的工作记录。具体操作如下图:
如今在开发时临时用户也增加到了业务需求中,为了应对需求变更我们不得不加入设计模式,为需求变更做好准备。新增加的临时用户的功能和其它几个功能的构件方法大致相同,而且要把建造和表示分离,如果在以后进行业务扩展时只需要在原有基础上加入新类来实现具体的建造过程即可,所以这时就引入了建造者模式。
结账构造类图:
结账业务过程:结账业务要求我们首先在选择框中选中要结账的操作员用户名,选择后会在购卡、充值、退卡、临时用户中显示该操作员的工作信息,并在汇总选项卡中显示汇总信息。
以临时用户为例,部分结账时序图:
建造者模式使得结账的构件过程和具体的表示进行了分离,分成了QryInfo()和WorkCash()两步来进行构造,使得杂论无章的构造过程变得有序,方便了以后新增功能,如增加消费时间表等的功能。
CheckOutDemoBLL代码具体实现过程,指图1中的构件方法类,根据用户的需求来构件不同的实现,里面的方法没有具体的实现,具体的实现封装在了图2或图3中。
Imports System.Data.SqlClient Imports Entity.ChargeSystem.Entity Imports Factory.ChargeSystem.Factory Imports IDAL.ChargeSystem.IDAL Namespace ChargeSystem.BLL ''' <summary> ''' 具体的结账类 ''' </summary> Public Class CheckOutDemoBLL Public m_CheckOutBLL As CheckOutBLL '聚合CheckOutBLL,将具体的需要建造的类传进来 '构造函数,初始化参数m_CheckOutBLL Sub New(check As CheckOutBLL) m_CheckOutBLL = check End Sub ''' <summary> ''' 结账建造过程,最精彩的一节。 ''' 结账过程步骤,将结账步骤分离成为查询和计算金额两部 ''' </summary> ''' <param name="Admin"></param> Public Function Checkout(ByVal Admin As AdminEntity) As Double Dim AllCash As Double '定义返回值,它是汇总表中的金额 Dim dt As DataTable '定义窗体给WorkAllCash()函数的参数 dt = m_CheckOutBLL.QryInfo(Admin) '为dt赋值 AllCash = m_CheckOutBLL.WorkAllCash(dt) '为AllCash赋值 Return AllCash End Function ''' <summary> ''' 显示数据 ''' </summary> ''' <param name="Admin"></param> Public Function ShowData(ByVal Admin As AdminEntity) As DataTable Return m_CheckOutBLL.QryInfo(Admin) End Function End Class ' CheckOutDemoBLL End Namespace ' BLL
CheckOutTmpStuBLL代码实现过程,里面封存了一般用户具体的结账过程,在客户端调用时将它传入CheckOutDemoBLL中进行实现。
Imports Entity.ChargeSystem.Entity Imports Factory.ChargeSystem.Factory Imports IDAL.ChargeSystem.IDAL Namespace ChargeSystem.BLL ''' <summary> ''' 临时用户结账功能 ''' </summary> Public Class CheckOutTmpStuBLL Inherits ChargeSystem.BLL.CheckOutBLL ''' <summary> ''' 按照管理员用户名查询相应记录 ''' </summary> ''' <param name="Admin"></param> Public Overrides Function QryInfo(ByVal Admin As AdminEntity) As DataTable Dim DataAccess As New DataAccess '定义抽象工厂类,用来实例化TmpStuDAL类 Dim sqlTmpStuDAL As ITempStuIDAL = DataAccess.CreateTmpStu '创建sqlTmpStuDAL类 Dim dt As DataTable = sqlTmpStuDAL.SelectByAdmin(Admin) '查询该管理员对临时用户的操作记录 Return dt '返回操作值 End Function ''' <summary> ''' 计算相应金额 ''' </summary> ''' <param name="dt"></param> Public Overrides Function WorkAllCash(ByVal dt As DataTable) As Double Dim dblAllCash As Double = 0 '定义上机金额 '遍历表,计算该管理员的所有关于临时用户的操作金额 For Each row As DataRow In dt.Rows dblAllCash = dblAllCash + CDbl(row(3)) Next Return dblAllCash '返回值 End Function End Class ' CheckOutTmpStuBLL End Namespace ' BLL
Imports System.Data.SqlClient Imports Entity.ChargeSystem.Entity Imports BLL.ChargeSystem.BLL Public Class frmSettleAccounts Private Sub cmbUserName_SelectedIndexChanged(sender As Object, e As EventArgs) Handles cmbUserName.SelectedIndexChanged Dim enAdmin As AdminEntity '定义需要传递的实体类 enAdmin.strUsername = txtUsername.Text.Trim() '为实体类用户名赋值 Dim CheckTmpStuBLL As New CheckOutTmpStuBLL '定义并实例化具体的建造者类,临时结账用户 Dim CheckOutDemoBLL As New CheckOutDemoBLL(CheckTmpStuBLL) '定义并实例化建造过程 Dim AllCash As Double = CheckOutDemoBLL.Checkout(enAdmin) '建造,实现金额汇总 End Sub End Class
在建造者模式中临时用户表示的是具体需要构件的类,通俗点说他就相当于我们图1中的圆或者长方形。它要传入CheckOutDemoBLL中实现具体的构件,CheckOutDemoBLL就相当于图1中的圆角矩形,在它中实现一步步的构件。那为什么CheckOutTmpStuBLL还要写具体的构造实现方法呢?因为,它和CheckOutDemoBLL是一种聚合关系,这种聚合关系将CheckOutTmpStuBLL传入CheckOutDemoBLL中,在CheckOutDemoBLL中使用CheckOutTmpStuBLL中的方法实现构造。
需求变更是一件很头疼的事,所以在设计系统时开发人员要反复的琢磨客户提出的需求,对需求有非常清楚的理解,并能够预见性的分析系统在哪些地方会出现业务的变更,及时和客户交流。在开发系统框架时应多采用设计模式,最大限度的减小在开发过程中需求的变更所带来的损失。