在机房收费系统中,学生下机结账最让人头疼了。因为学生的消费时间(下机时间-上机时间-上机准备时间)有三种情况:
1.消费时间<=0,也就是下机时间与上机时间的间隔在上机准备时间范围内,这种情况是不收费的。
2.消费时间<=最少上机时间,这种情况下消费时间按照最少上机时间收费。
3.消费时间>最少上机时间,这种情况还要考虑递增单位时间。比如递增单位时间是10分钟,消费时间是42分
钟,42/10=4.....2,这种情况要按照50分钟收费。
以上消费时间就分了三种情况,更复杂的是第二种和第三种情况还需要分别考虑用户类型是固定用户还是临时用
户,这两种用户类型的费率不一样。在前两次的机房收费系统中,鄙人花费了大量的时间和精力,整出大量的、嵌套
的if语句来计算消费。if语句用多了,程序就有坏味道了。既然学习了设计模式,就要运用到实践中。
职责链模式的定义是:使多个对象都有机会处理请求,从而避免请求的发送者和接受者之间的耦合关系。将这个
对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。在机房收费系统下机结账中,“请求”中
携带着用户消费时间和用户类型,“处理”就是三种不同消费时间下的不同计费方式。请求在这三种处理中传递,
消费时间符合哪种处理,就到哪种处理方法中计费。下面展示类图和具体的代码:
Request:
<span style="font-family:Microsoft YaHei;font-size:18px;">Public Class Request ''' <summary> ''' 卡类型 ''' </summary> Private cardType As String ''' <summary> ''' 消费时间,单位分 ''' </summary> Private consumeTime As Integer ''' <summary> ''' 卡类型 ''' </summary> Public Property ProcardType() As String Get Return cardType End Get Set(ByVal Value As String) cardType = Value End Set End Property ''' <summary> ''' 消费时间,单位分 ''' </summary> Public Property ProconsumeTime() As Integer Get Return consumeTime End Get Set(ByVal Value As Integer) consumeTime = Value End Set End Property End Class ' Request</span>
ChargeMethod:
<span style="font-family:Microsoft YaHei;font-size:18px;">''' <summary> ''' 抽象类,计费方法 ''' </summary> Public MustInherit Class ChargeMethod ''' <summary> ''' 上级 ''' </summary> Protected superior As ChargeMethod Public MustOverride Sub SetSuperior(ByVal superior As ChargeMethod) ''' <summary> ''' 虚方法,子类重写 ''' </summary> ''' <param name="request"></param> Public MustOverride Function RequestApplication(ByVal request As Request) As Decimal End Class ' ChargeMethod</span>
ChargeMethodA:
<span style="font-family:Microsoft YaHei;font-size:18px;">''' <summary> ''' 没有消费时间的方法 ''' </summary> Public Class ChargeMethodA : Inherits Pack.BLL.ChargeMethod ''' <summary> ''' 计费方式一:在准备上机时间范围内不收钱,消费为0 ''' </summary> ''' <param name="request"></param> Public Overrides Function RequestApplication(ByVal request As Request) As Decimal If (request.ProconsumeTime <= 0) Then Return 0 Else Return Me.superior.RequestApplication(request) End If End Function Public Overrides Sub SetSuperior(ByVal superior As ChargeMethod) Me.superior = superior End Sub End Class ' ChargeMethodA</span>
ChargeMethodB:
<span style="font-family:Microsoft YaHei;font-size:18px;">''' <summary> ''' 消费时间不超过最少上机时间 ''' </summary> Public Class ChargeMethodB : Inherits Pack.BLL.ChargeMethod ''' <summary> ''' 计费方式二:消费时间小于至少上机时间,按照至少上机时间计费 ''' </summary> ''' <param name="request"></param> Public Overrides Function RequestApplication(ByVal request As Request) As Decimal If (request.ProcardType = "固定用户" And request.ProconsumeTime <= leastTime) Then Return rate * leastTime / 60 ElseIf (request.ProcardType = "临时用户" And request.ProconsumeTime <= leastTime) Then Return tmpRate * leastTime / 60 Else Return Me.superior.RequestApplication(request) End If End Function Public Overrides Sub SetSuperior(ByVal superior As ChargeMethod) Me.superior = superior End Sub End Class ' ChargeMethodB</span>
ChargeMethodC:
<span style="font-family:Microsoft YaHei;font-size:18px;"> ''' <summary> ''' 计费方式三:消费时间大于至少上机时间的计费方法 ''' </summary> ''' <param name="request"></param> Public Overrides Function RequestApplication(ByVal request As Request) As Decimal Dim unitCount As Integer '消费时间中含有递增单位时间的个数 If (consumeTime / unitTime = 0) Then unitCount = consumeTime / unitTime Else unitCount = Fix(consumeTime / unitTime) + 1 End If If (request.ProcardType = "固定用户" And request.ProconsumeTime > leastTime) Then Return rate * unitCount * unitTime / 60 ElseIf (request.ProcardType = "临时用户" And request.ProconsumeTime > leastTime) Then Return tmpRate * unitCount * unitTime / 60 End If End Function Public Overrides Sub SetSuperior(ByVal superior As ChargeMethod) Me.superior = superior End Sub End Class ' ChargeMethodC</span>
具体使用的时候,代码如下:
<span style="font-family:Microsoft YaHei;font-size:18px;">Dim a As New Pack.BLL.ChargeMethodA Dim b As New Pack.BLL.ChargeMethodB Dim c As New Pack.BLL.ChargeMethodC Dim request As New Pack.BLL.Request Dim consumeCash As Decimal '消费金额 '设置链的传递方式,方式1→方式2→方式3 a.SetSuperior(b) b.SetSuperior(c) request.ProcardType = "临时用户" request.ProconsumeTime = 42 '单位:分钟 consumeCash = a.RequestApplication(request)</span>
这样做虽然创造了好多个类,但把if都封装了,对待所有的学生都一视同仁,不必考虑他属于哪种处理情况,复杂
的计费过程交给程序自己默默地判断、计算。使用职责链模式最重要的有两点:一个是事先给每个具体处理设置它
的"后继者",也就是链传递的下一个对象。另一个是在每个具体处理中对请求做出判断,是可以处理呢,还是转移到
它的后继者。本例中用户类型就两种,如果三种、四种......呢?这个时候可以引入策略模式和简单工厂。通过这次大胆
尝试职责链模式的应用,深刻体会到实践的重要性。