在第一遍学习大话设计模式的时候,写过关于状态模式的博客。不过那个时候的素材主要来自于课本,理解的内容也是书上的例子,以“看懂”为主要目的。但是在这次机房收费系统合作版的实战中,我需要以“会用”为主要目的了,当然,理解的也会有所深刻。
此次我把状态模式加在上学生下机处,主要为了计算消费金额。大致思路为:(此种思路主要针对于网吧使用的收费系统,因为学校机房可以玩通宵的还是比较少的。)首先,将上机状态分为两种:一种是白天状态,另一种那个就是夜间状态。夜间状态的消费金额为固定,白天状态的消费金额则是变化的,因消费时间和用户类型而异。下面是机房收费系统中状态模式的结构图:
代码如下:
一、抽象状态类:此类定义一个接口,封装了与Context中一个特定状态有关的行为。
''' <summary>
''' 抽像状态类
''' </summary>
Public MustInheritClass State
''' <summary>
''' 消费金额
''' </summary>
''' <paramname="enStudent">传递过来上机时间。</param>
Public MustOverride Function Handle(ByValenStudent As Entity.LineEntity, ByVal context As Context) As String
End Class
二、状态子类:夜间状态,此处取出上机时间中的小时值,判断用户处于哪儿中状态,如果符合当前子类状态,则返回当前状态,否则,进入预先设定的下一状态:白天模式。(注意状态子类中需要指定下一状态)
''' <summary>
''' 夜间状态'''</summary>
Public ClassNightState
Inherits BLL.State
''' <summary>
''' 消费金额
''' </summary>
''' <paramname="enStudent">传递过来上机时间。</param>
''' <paramname="context">状态的作用对象。</param>
Public Overloads Overrides FunctionHandle(enStudent As Entity.LineEntity, context As Context) As String
Dim SpendMoney As String
Dim onTime As System.DateTime
Dim hour As Integer = onTime.Hour
'取出上机时间中的小时值
onTime =System.DateTime.Parse(CStr(enStudent.OnTime))
'判断用户是何时上机,如果是夜间上机的话,返回“夜间模式”
If hour < 6 Or hour > 22 Then
Return "夜间模式"
Else
context.SetState(New DayState)
End If
End Function
End Class
三、状态子类:同上
''' <summary>
''' 白天模式'''</summary>
Public ClassDayState
Inherits BLL.State
''' <summary>
''' 消费金额
''' </summary>
''' <paramname="enStudent">传递过来上机时间。</param>
''' <paramname="context">状态作用对象</param>
Public Overloads Overrides FunctionHandle(enStudent As Entity.LineEntity, context As Context) As String
Dim onTime As System.DateTime
Dim hour As Integer = onTime.Hour
'取出上机时间中的小时值
onTime =System.DateTime.Parse(CStr(enStudent.OnTime))
'判断用户是何时上机,如果是夜间上机的话,消费时间为实际消费时间,消费金额固定为15元
If hour > 6 And hour < 22 Then
Return "白天模式"
Else
context.SetState(New NightState)
End If
End Function
End Class
四、Context类。首先,它需要定义一个初始状态,再此我们定义DayState也就是白天模式为默认初始状态,并且需要设置一个“钟点”属性,用来判断当前状态和设置最新状态。
''' <summary>
'''Context类,定义当前的状态
''' </summary>
'''<remarks></remarks>
Public Class Context
Private currentState As State
'定义Context的初始状态--为白天模式
Public Sub New(ByVal State As State)
currentState = New DayState
End Sub
Private hour As Double
'"钟点"属性,状态转换的依据
Public Property _Hour() As Double
Get
Return hour
End Get
Set(ByVal value As Double)
hour = value
End Set
End Property
''' <summary>
''' 设置工作状态
''' </summary>
''' <paramname="state">工作状态</param>
''' <remarks></remarks>
Public Sub SetState(ByVal state As State)
currentState = state
End Sub
''' <summary>
''' 维护一个ConcreteState子类的实例,这个实例定义当前的状态。
''' </summary>
Public Function Request(ByVal enStudent AsEntity.LineEntity) As String
'对请求做处理,并设置下一状态
Return currentState.Handle(enStudent,Me)
End Function
End Class
五、客户端代码:此处,实例化Context类,并且设置Context的初始状态为:白天状态,然后根据U层传来的学生上机信息实体类中的上机时间,来判断属于那种状态。如果是白天状态,则调用策略模式中的相应具体算法类(此处咱不强调),根据消费时间和用户类型,计算出消费金额;反之如果是夜间状态,暂定消费金额固定,为15元。
''' <summary>
''' 不同的用户收取的金额。
''' </summary>
''' <paramname="enStudent">学生上机实体类</param>
Public Function GetResutl(ByVal enStudentAs Entity.LineEntity) As Double
Dim DayState As New DayState
Dim stateType As NewContext(DayState) '设置Context的初始状态为:白天模式
Dim NowState As String
Dim SpendMoney As Double
NowState =stateType.Request(enStudent) '根据实体中的上机时间属性,来判断状态类型
'如果是夜间模式,消费金额恒为15元;如果为白天模式,消费金额依据卡类型不同而不同
If NowState = "白天模式" Then
'固定用户调用FixUser具体算法类
IfenStudent.CardType = "固定用户" Then
Dim fixUser As New FixUser
SpendMoney =fixUser.AcceptCash(enStudent)
Else
'临时用户调用TempUser具体算法类
Dim tempUser As New TempUser
SpendMoney =tempUser.AcceptCash(enStudent)
End If
Else
SpendMoney = 15
End If
Return SpendMoney
End Function
这样,我们可以实现代码扩展的灵活性,我们可以通过增加不同的状态子类来增加不同的状态,不再需要过丑和过长的分支判断了。当然了,这样也实现了好代码的可开放-封闭原则。