一、前言
前面我们学习了23种设计模式,只是一直没用过,这次机房重构就是让我们将学习的这些理论应用与实践。首先,机房收费的主要功能就是上机收费,一说的收费就会有针对普通用户和会员等,实行不同的收费方法,这时我们就须要用到策略模式了。
二、回想
策略模式定义了一系列的算法,并将每个算法封装起来,并且使它们还能够相互替换。策略模式让算法独立于使用它的客户而独立变化。
详见:设计模式 ( 十八 ) 策略模式Strategy(对象行为型)
三、代码实现(为了方便,将整个下机过程的代码所有给出)
1、U层中下机button的代码
''' <summary>
''' 下机功能
''' </summary>
''' <param name="sender"></param>
''' <param name="e"></param>
''' <remarks></remarks>
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Dim facadeSystem As New FacadeSystem
Dim cardEntity As New CardEntity
Try
cardEntity.CardId = txtCardId.Text '传參
Dim returnOnList As ArrayList
'调用外观,实现下机
returnOnList = facadeSystem.StudentOff(cardEntity)
'提出返回的数据
Dim returnStudentEntity As StudentEntity
Dim returnLineRecordEntity As LineRecordEntity
Dim returnCardEntity As CardEntity
returnStudentEntity = returnOnList.Item(0)
returnLineRecordEntity = returnOnList.Item(1)
returnCardEntity = returnOnList.Item(2)
'显示学生信息
txtStudentId.Text = returnStudentEntity.StudentId
txtName.Text = returnStudentEntity.StudentName
txtSex.Text = returnStudentEntity.Sex
txtClass.Text = returnStudentEntity.Classes
txtDepartment.Text = returnStudentEntity.Department
txtExplan.Text = returnStudentEntity.Explain
txtGrade.Text = returnStudentEntity.Grade
'显示上机信息
txtOnTime.Text = returnLineRecordEntity.OnTime
txtOffTime.Text = returnLineRecordEntity.OffTime
txtConsumeCash.Text = returnLineRecordEntity.ConsumeCash
txtConsumeTime.Text = returnLineRecordEntity.ConsumeTime
txtRemainCash.Text = returnCardEntity.RemainCash
'显示上机人数
lbOnPeople.Text = facadeSystem.RefreshOnPeople()
'显示上机成功
lbInformation.Text = "欢迎下次光临!"
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Sub
2、外观层代码
''' <summary>
''' 学生下机
''' </summary>
Public Function StudentOff(ByVal cardEntity As CardEntity) As ArrayList
Try
Dim cardBLL As New CardBLL
Dim returnCardEntity As New CardEntity
Dim returnBoolean As Boolean
'检查卡号是否存在
returnBoolean = cardBLL.QueryCard(cardEntity)
If Not returnBoolean Then
Throw New Exception("卡号不存在!")
End If
'检查该卡号是否正在上机
Dim lineRecordBll As New LineRecordBLL
returnBoolean = lineRecordBll.GetOffStatus(cardEntity)
If Not returnBoolean Then
Throw New Exception("该卡号还没上机!")
End If
'假设正在上机,就查到上机信息
Dim lineRecordEntity As New LineRecordEntity
lineRecordEntity = lineRecordBll.GetOff(cardEntity)
'查看基础数据设定
Dim basicDataEntity As New BasicDataEntity
Dim basicDataBll As New BasicDataBLL
basicDataEntity = basicDataBll.QueryAll()
'计算消费时间
Dim getDateBll As New GetDateBLL
Dim strPrepareTime As Double
Dim consumeTime As Double
lineRecordEntity.OffTime = getDateBll.GetSqlTime() '获取下机时间
strPrepareTime = getDateBll.changeForHour(basicDataEntity.PrepareTime) '将准备时间转换为以小时为单位
consumeTime = getDateBll.TimeToHour(lineRecordEntity.OffTime) - getDateBll.TimeToHour(lineRecordEntity.OnTime) '计算消费时间
'推断消费时间是否大于准备时间
If consumeTime < strPrepareTime Then
lineRecordEntity.ConsumeTime = "0.0"
Else
lineRecordEntity.ConsumeTime = CStr(consumeTime - strPrepareTime)
End If
'计算消费金额
Dim strStudentLevel As String = "GeneralUser" '用来实例化详细的计费方法
Dim cashContext As CashContext
cashContext = New CashContext(strStudentLevel) '实例化计费方法的类
lineRecordEntity.ConsumeCash = CStr(cashContext.GetResult(basicDataEntity, lineRecordEntity))
'其它參数
lineRecordEntity.OffDate = getDateBll.GetSqlDate()
lineRecordEntity.OffStatus = "正常下机"
lineRecordEntity.Local = System.Net.Dns.GetHostName().ToString
'记录为正常下机
lineRecordBll.AddOff(lineRecordEntity)
'改动卡上剩余金额
cardEntity = cardBLL.QueryAll(cardEntity)
cardEntity.RemainCash = CStr(CDbl(cardEntity.RemainCash) - CDbl(lineRecordEntity.ConsumeCash))
cardBLL.ModifyRemainCash(cardEntity)
'查询学生实体
Dim studentEntity As New StudentEntity
Dim studentBll As New StudentBLL
studentEntity.StudentId = cardEntity.CardId
studentEntity = studentBll.QueryAll(studentEntity)
'封装,传回
Dim returnOnList As New ArrayList
returnOnList.Add(studentEntity)
returnOnList.Add(lineRecordEntity)
returnOnList.Add(cardEntity)
Return returnOnList
Catch ex As Exception
Throw
End Try
End Function
3、cashContent类(相当于策略模式图中的Context类
''' <summary>
''' 策略模式中的context类,用一个详细计费方法来配置,维护一个对抽象计费方法对象的引用
''' </summary>
Public Class CashContext
''' <summary>
''' 声明一个计算消费金额的抽象类
''' </summary>
Private cashSuper_ As CashSuper
Private _strStudentLevel As String
''' <summary>
''' 声明一个计算消费金额的抽象类
''' </summary>
Private Property cashSuper() As CashSuper
Get
Return cashSuper_
End Get
Set(ByVal Value as CashSuper)
cashSuper_ = Value
End Set
End Property
''' <summary>
''' 通过构造方法,传入详细的收费策略
''' </summary>
''' <param name="strStudentLevel">初始化时,传入一个详细的收费策略</param>
Sub New(strStudentLevel As String)
Try
'利用反射创建详细的算法
Dim AssemblyName As String = "BLL"
Dim className As String = AssemblyName + ".BLL." + strStudentLevel + "ConsumeCash"
Me.cashSuper_ = CType(Assembly.Load(AssemblyName).CreateInstance(className), CashSuper)
Catch ex As Exception
Throw
End Try
End Sub
''' <summary>
''' 依据不同的收费策略,获得计算结果
''' </summary>
''' <param name="basicDataEntity"></param>
''' <param name="lineRecordEntity"></param>
''' <returns></returns>
''' <remarks></remarks>
Public Function GetResult(ByVal basicDataEntity As BasicDataEntity, ByVal lineRecordEntity As LineRecordEntity) As Double
Try
Dim returnDouble As Double
returnDouble = cashSuper_.CountConsumeCash(basicDataEntity, lineRecordEntity)
Return returnDouble
Catch ex As Exception
Throw
End Try
End Function
End Class ' CashContext
4、CashSuper抽象类(相相当于策略模式图中的Strategy类
''' <summary>
''' 策略模式,封装计算方法的抽象类
''' </summary>
Public MustInherit Class CashSuper
''' <summary>
''' 计算消费金额的计算方法
''' </summary>
''' <param name="lineRecordEntity">上机记录表的实体</param>
''' <param name="basicDataEntity">数据库数据表实体</param>
Public MustOverride Function CountConsumeCash(ByVal basicDataEntity As BasicDataEntity, ByVal lineRecordEntity As LineRecordEntity) As Double
End Class ' CashSuper
5、GeneralUserConsumeCash类(相相当于策略模式图中的CreateStrategy类,详细收费方法
''' <summary>
''' 计算一般用户消费的方法
''' </summary>
Public Class GeneralUserConsumeCash
Inherits BLL.CashSuper
''' <summary>
''' 计算消费金额的计算方法
''' </summary>
''' <param name="lineRecordEntity">上机记录表的实体</param>
''' <param name="basicDataEntity">数据库数据表实体</param>
Public Overrides Function CountConsumeCash(ByVal basicDataEntity As BasicDataEntity, ByVal lineRecordEntity As LineRecordEntity) As Double
Try
Dim returnMoney As Double
Dim countTime As Integer '表示多少个单位时间
Dim oneMinuteMoney As Double '每分钟消费的钱
oneMinuteMoney = CDbl(basicDataEntity.UnitCash) / 60
'推断是否在规定上机时间内
If lineRecordEntity.ConsumeTime > basicDataEntity.LeastTime Then '假设超过至少上机时间
'单位时间的个数=(消费时间-至少上机时间)/单位时间
countTime = CInt(Int((CDbl(Minute(CDate(lineRecordEntity.ConsumeTime))) - CDbl(basicDataEntity.LeastTime)) / basicDataEntity.UnitTime + 0.9999))
'消费金额=(单位时间个数*单位时间+至少上机时间)*每分钟消费的钱
returnMoney = (countTime * basicDataEntity.UnitTime + CDbl(basicDataEntity.LeastTime)) * oneMinuteMoney
Return returnMoney
Else '假设没有超过至少上机时间
'至少上机时间乘以单位时间的钱
returnMoney = CDbl(basicDataEntity.LeastTime) * oneMinuteMoney
Return returnMoney
End If
Catch ex As Exception
Throw
End Try
End Function
End Class ' GeneralUserConsumeCash
四、讲解运行过程
首先,将 "GeneralUser"传给变量strStudentLevel,作用是在Dim className As String = AssemblyName + ".BLL." + strStudentLevel + "ConsumeCash"这句话中,利用反射创建详细的收费方法,即普通用户的收费方法GeneralUserConsumeCash类。
然后,调用cashContext.GetResult()方法,此方法又调用抽象类cashSuper的计算消费金额CountConsumeCash
的方法,因为此方法在子类中被重写,所以最后运行详细收费类GeneralUserConsumeCash的CountConsumeCash方法,从而得出结果。
的方法,因为此方法在子类中被重写,所以最后运行详细收费类GeneralUserConsumeCash的CountConsumeCash方法,从而得出结果。
五、总结
本来能够通过訪问配置文件向cashContext中传入參数,从而推断详细的收费方法,这样当我们须要改动收费方法的时候直接改动配置文件即可了,使我们的维护更加的方便。因为这次仅仅使用了普通用户,所以计费方法仅仅有一种,仅仅採用的strStudentLevel变量传參,大大降低了系统的可维护性。所以在以后,我们做系统的时候一定要注意,尽量降低系统的耦合度,从而创造出更加优秀的产品。