一、前言
前面我们学习了23种设计模式,不过一直没用过,这次机房重构就是让我们将学习的这些理论应用与实践。首先,机房收费的主要功能就是上机收费,一说的收费就会有针对普通用户和会员等,实行不同的收费方法,这时我们就需要用到策略模式了。
二、回顾
策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
详见:设计模式
( 十八 ) 策略模式Strategy(对象行为型)
三、代码实现(为了方便,将整个下机过程的代码全部给出)
1、U层中下机按钮的代码
''' <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方法,从而得出结果。
五、总结
本来可以通过访问配置文件向cashContext中传入参数,从而判断具体的收费方法,这样当我们需要修改收费方法的时候直接修改配置文件就行了,使我们的维护更加的方便。由于这次只使用了普通用户,所以计费方法只有一种,仅仅采用的strStudentLevel变量传参,大大降低了系统的可维护性。所以在以后,我们做系统的时候一定要注意,尽量减少系统的耦合度,从而创造出更加优秀的产品。