引言
对于重构来说我们知道在机房收费这个系统中有三种下机——正常下机、强制所有下机、选择强制下机。在第一遍的时候我们就是在做完正常下机以后,然后复制代码来完成,在重构的时候有了设计模式作为基础,我们可以用观察者模式轻松搞定这三种下机。
基础篇
基本概念:观察者模式又叫做发布-订阅模式,它定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化的时,会通知所有的观察者对象,是他们能够自动更新自己。
基本类图:
应用场景:当一个对象的改变需要同时改变其他对象,并且他不知道具体有多少对象有待改变时,应该考虑使用观察者模式。总的来讲,观察者模式所做的工作其实就是子啊解除耦合,让耦合的双方都依赖于抽象而不是依赖于具体,从而是得各自的变化都不会影响另一边的变化!
实战篇
在上面介绍了关于观察者模式的一些基本知识,现在就借助机房重构这个机会小试一把。因为我在下机的过程中需要进行如下判断:卡号是否存在、卡号是否正在上机、是否插入上机记录表等。这些都是每个下机都要判断的,所以用观察者模式来解决这个问题。
类图如下:
部分代码如下:
'**********************************************
'说明:在下机的时候用观察者模式,抽象观察者
'命名空间:BLL
'机器名称:晓
'创建日期:2015/1/4 22:50:10
'作者:郑浩
'版本号:V1.00
'**********************************************
Public MustInherit Class SubjetBll
Private ilist As IList(Of ObserverBLL) = New List(Of ObserverBLL)
''' <summary>
''' 添加观察者
''' </summary>
''' <param name="observer">抽象观察者类</param>
Public MustOverride Sub Attach(ByVal observer As ObserverBLL)
''' <summary>
''' 去除观察者
''' </summary>
''' <param name="observer">抽象观察者类</param>
Public MustOverride Sub Detach(ByVal observer As ObserverBLL)
''' <summary>
''' 通知观察者
''' </summary>
Public MustOverride Sub Notify(ByVal online As Entity.EN_CardInfo)
End Class
'**********************************************
'说明:抽象的观察者
'命名空间:BLL
'机器名称:晓
'创建日期:2015/1/4 22:53:55
'作者:郑浩
'版本号:V1.00
'**********************************************
Public MustInherit Class ObserverBLL
'定义一个抽象的更新的方法
Public MustOverride Sub Update(ByVal online As Entity.EN_CardInfo)
End Class
说明:上机的第一步检查卡号时候存在
'命名空间:BLL
'机器名称:晓
'创建日期:2015/1/3 8:20:17
'作者:郑浩
'版本号:V1.00
'**********************************************
Imports IDAL
Imports BLL.OnLineStateBLL
Public Class IsExitCardNoBLL : Inherits OnLineStateBLL
Public Overrides Sub online(ByVal cardinfo As Entity.EN_CardInfo, ByVal upline As BLL.UpLineBLL)
Dim factory As New DAL.Factory
Dim Ionline As IOnOffLine
Ionline = factory.CreateOnOffLine
Dim dt As DataTable
dt = Ionline.CheckCardInfo(cardinfo)
'判断卡号是否存在
If (dt.Rows.Count = 0) Then
MsgBox("卡号不存在")
Else
cardinfo.StudentName = dt.Rows(0).Item(3)
cardinfo.Status = "正常上机"
upline.SetNextState(New BalanceStateBLL)
upline.CardOnLine(cardinfo)
End If
End Sub
End Class
'**********************************************
'说明:插入上机记录
'命名空间:BLL
'机器名称:晓
'创建日期:2015/1/5 23:45:31
'作者:郑浩
'版本号:V1.00
'**********************************************
Imports BLL.ObserverBLL
Imports IDAL
Public Class OInsertOnLineRecord : Inherits BLL.ObserverBLL
Public Overrides Sub Update(online As Entity.EN_CardInfo)
If Entity.PublicVariables.observeflag = "用户未上机" Then
Else
'首先在上机表中读取数据,然后赋给实体
Dim factory As New DAL.Factory
Dim Ionlining As IOnLining
Ionlining = factory.CreateOnLining
Dim dt As DataTable
dt = Ionlining.IsCardNoLining(online)
'计算消费时间
Dim offdate As Date '下机日期
offdate = Date.Today()
Dim offtime As String '下机时间
offtime = Date.Now.ToString("hh:mm:ss")
Dim spenttime As Double '消费的时间
Dim realitytime As Double '实际消费时间
Dim spentmoney As Integer '消费金额
Dim type As String '用户类型
Dim cardbalance As Integer
'调用获得基本数据方法来获得基本数据
Dim basicdatafactory As New DAL.Factory
Dim Ibasicdata As ISetBasicData
Ibasicdata = basicdatafactory.CreateBasicData
Dim table As DataTable
table = Ibasicdata.GetBasicData()
'计算实际消费时间
realitytime = 0
spenttime = DateDiff(DateInterval.Minute, dt.Rows(0).Item(3), offdate) + DateDiff(DateInterval.Minute, CDate(dt.Rows(0).Item(4)), CDate(offtime))
'判断消费时间和准备时间的大小
If (spenttime < table.Rows(0).Item(5)) Then
realitytime = 0
ElseIf (spenttime < table.Rows(0).Item(4)) Then
realitytime = table.Rows(0).Item(4)
Else
realitytime = spenttime
End If
'调用检查卡号的方法,来获取用户类型和余额
Dim cardfactory As New DAL.Factory
Dim Ionline As IOnOffLine
Ionline = cardfactory.CreateOnOffLine
Dim carddt As DataTable
carddt = Ionline.CheckCardInfo(online)
type = carddt.Rows(0).Item(15) '获得用户类型
cardbalance = carddt.Rows(0).Item(8) '获得卡内余额
Dim basicdata As New Entity.EN_BasicData
basicdata.FixPrice = table.Rows(0).Item(1)
basicdata.TempPrice = table.Rows(0).Item(2)
'调用CashFactory 来计算消费金额
Dim cashfactory As BLL.CashFactory = New CashFactory()
Dim csuper As BLL.CashSuper
csuper = cashfactory.CreateCashAccept(type)
spentmoney = csuper.AcceptCash(basicdata, realitytime)
'定义一个实体用来插入上机记录
Dim onlinerecord As New Entity.EN_CardInfo
onlinerecord.CardNo = dt.Rows(0).Item(1)
onlinerecord.StudentName = dt.Rows(0).Item(2)
onlinerecord.OnDate = dt.Rows(0).Item(3)
onlinerecord.OnTime = dt.Rows(0).Item(4)
onlinerecord.OffDate = offdate
onlinerecord.OffTime = offtime
onlinerecord.ConsumeTime = spenttime
onlinerecord.ConsumeMoney = spentmoney
onlinerecord.Balance = cardbalance - spentmoney
onlinerecord.WorkerComputer = System.Net.Dns.GetHostName().ToString()
onlinerecord.Status = online.Status
onlinerecord.Type = type
onlinerecord.UserName = Entity.PublicVariables._username
'用于传给U层显示在文本框中
Entity.PublicVariables.onlinerecordinfo = onlinerecord
'调取学生表中的信息,用于填写U层的学生的基本信息
Dim Cfactory As New DAL.Factory
Dim CIonline As IOnOffLine
CIonline = factory.CreateOnOffLine
Dim Cdt As DataTable
Cdt = Ionline.CheckCardInfo(online)
'获得学生的基本信息后,赋给全局变量,然后传递给U层
Entity.PublicVariables.StudentBasicInfo = Cdt
'更新上机记录表中的数据
Dim Ionlinerecord As IOnOffLine
Dim result As Integer
Ionlinerecord = factory.CreateOnOffLine
result = Ionlinerecord.UpdateLineRecord(onlinerecord)
If result = 0 Then
MsgBox("更新上机记录失败")
End If
End If
End Sub
End Class
<pre name="code" class="vb">**********************************************
'说明:具体的观察者
'命名空间:Facade
'机器名称:晓
'创建日期:2015/1/6 0:23:04
'作者:郑浩
'版本号:V1.00
'**********************************************
Public Class FacadeFormalOnLine
''' <summary>
''' 正常上机
''' </summary>
''' <param name="online"></param>
''' <remarks></remarks>
Public Sub FormalOnLine(ByVal online As Entity.EN_CardInfo)
Dim Fformalonline As New BLL.NormalOffLine
Fformalonline.Attach(New BLL.OCardIsExist)
Fformalonline.Attach(New BLL.OCardIsOnLine)
Fformalonline.Attach(New BLL.OInsertOnLineRecord)
Fformalonline.Notify(online)
End Sub
End Class
思考
我们都已经做过了一遍机房收费系统,我们根据需求会发现这样一个问题,当我们判断到卡号不存在时,会给出一个提醒“卡号不存在”,这时候后面的判断就不用进行了,直接退出,但是在观察这模式会自动遍历这些观察者,这就造成重复提示的缺陷,为了避免这个缺陷,我在实体层定义了一个全局变量用来在B层控制是否进行下一个判断。虽然达到做了目的,但是总感觉这样的办法不是最好的,这个问题正在研究中,如果哪位大神有好的想法,请留言提示!
小结
其实下机和上机时计算消费的判断思路差不多,但是为了更好的联系设计模式的知识,所以采用了不同的模式,在上机计算消费时采用了状态模式,详见【机房重构】——策略模式+简单工厂计算消费,在重构的时候主要是对前面学习的这些编程的思想和知识的一个灵活运用,所以在做的时候尽可能的采用不同思路和想法从多个方面来完成相应的功能,在这个阶段的运用没有对错之分,只有用过以后才能在下一次编程的时候更加灵活的运用它,所有希望同学能打开思路进行机房重构,Come On!!1