需求介绍:

某学校实现网上订餐后,到食堂刷卡就餐。可是为了督促大家要先在网上订餐后再去食堂就餐(食堂可以实现按实际所需做饭),对于未订餐的要进行处罚;还有一种情况是订餐未就餐的情况;当然最后是正常就餐(也就是订餐后就餐)。学校希望对于前两种情况进行相应的处罚,比如说:三天不允许订餐,当然也就不允许就餐、或者是罚款、再或者是先警告指定次数,无效后再进行处罚(也就是罚款或者几天不允许订餐)。

说实话这个需求着实让我头疼了好几天啊!我们拿到这个需求后,第一印象也许就是要用策略模式进行处理,可是当时我又这样想,对于这两种处罚情况(订餐未就餐,未订餐就餐),要执行不同的操作啊!比如说对于现金处罚来说吧:如果是对于订餐未就餐的情况,那么应该把这些未处理的订单的状态改为处理,而未订餐就餐因为没有订单就不用执行这个操作。

         也就是因为这样想,对于每一种处罚类型,它的实现也是不一样的,所以当时犹豫了一下,想用抽象工厂模式来解决这个问题。可是抽象工厂是用来解决多个抽象产品的问题,对于多个抽象产品的实现存在不小的差异,而且两种产品不是经常变化的。如果真的用抽象工厂来解决这个问题的话,那么将会产生大量的重复代码,那是很糟糕的。

那么我们再来看看策略模式。  

策略模式定义了算法家族,分别封装起来,让它们之间可以相互替换,此模式让算法的变化,不会影响到使用算法的客户。

策略模式是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

经过仔细地分析,我们不难发现,这里运用策略模式来解决是比较合理的,那么,对于这两种情况(订餐未就餐处罚、未订餐就餐处罚),怎么进行代码的复用呢?不要着急,我们接下来就会解决这个问题。

我们先来看下基本的策略模式图:

        订餐系统总结(三)策略模式应用于处罚方案管理_字段

其实开始的时候,自己的头脑一直特别的乱,是因为我把这三种情况(正常就餐、未订餐就餐、订餐未就餐)一起来考虑的,让自己走进了一个死胡同。后来突然开窍了, 既然是这三种情况,我们为什么不一个个的考虑,然后逐个击破呢?所以,顺着这个思路,一个复杂的问题,拆分成多个不同的情况,每种情况再进行不同的处理。

处理流程:

当用户刷卡就餐时,进行查询判断,如果发现有订餐未就餐记录,则跳转到订餐未就餐的处罚界面(在这里查询订单状态为“未处理”的数量,如果发现大于0,则一定存在订餐未就餐的情况,同时这个字段也做为 处罚记录表 的一个字段,对于解决上述的复用问题,这个字段也起着关键的作用

下面是PunishContext的代码,这个项目运用了三层,底层用了抽象工厂。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using IDAL; using System.Data; using Entity; namespace Strategy { public class PunishContext { PunishSuper ps; IPunishData IPunishData; IActionOfPunish IActionOfPunish; public PunishContext(enPunisRecord PunishRecord) { IPunishData = DBFactory.DBFactory.CreatePunishData(); IActionOfPunish = DBFactory.DBFactory.CreateActionOfPunish(); int punishDataId = IActionOfPunish.GetPunisDataId(PunishRecord.PunishAction); //该方案下的处罚类型 DataTable dt = IPunishData.SelectById(punishDataId); string type =dt .Rows[0]["punishType"].ToString().Trim(); switch (type) { case "现金处罚": PunishRecord.PunishType = "现金处罚"; ps = new CashPunish(PunishRecord); break; case "停卡处罚": PunishRecord.PunishType = "停卡处罚"; ps = new StopCardPunish(PunishRecord); break; case "警告后停卡处罚": PunishRecord.PunishType = "警告后停卡处罚"; ps = new WarnStopCardPunish(PunishRecord); break; case "警告后现金处罚": PunishRecord.PunishType = "警告后现金处罚"; ps = new WarnCashPunish(PunishRecord); break; } } public enPunisRecord GetResult() { return ps.Punish(); } } }

个类的主要是作用是用来决定产生哪种处罚子类。

下面是punishSuper类的方法:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using Entity; namespace Strategy { public abstract class PunishSuper { public abstract enPunisRecord Punish(); } } 为了举例说明,下面只展示 一个现金处罚类

using System; using System.Collections.Generic; using System.Linq; using System.Text; using Entity; using IDAL; using System .Data; using System.Data.SqlClient; using System.Configuration; namespace Strategy { public class CashPunish:PunishSuper { private enPunisRecord PunishRecord; public CashPunish(enPunisRecord PunishRecord) { this.PunishRecord = PunishRecord; } public override enPunisRecord Punish() { //根据处罚行为获取该处罚方案下的参数信息 datatable DataTable dt=GetParameter(PunishRecord.PunishAction); PunishRecord.PunishCash =PunishRecord.PunishCash+Convert.ToDouble(dt.Rows[0]["PunishCash"]); IPunishRecord IPunishRecord; IPunishRecord = DBFactory.DBFactory.CreatePunishRecord(); IOrderInfo IOrderInfo; IOrderInfo = DBFactory.DBFactory.CreateOrderInfo(); ICardInfo ICardInfo; ICardInfo = DBFactory.DBFactory.CreateCardInfo(); SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["strConnection"].ConnectionString); SqlTransaction tran; conn.Open(); tran = conn.BeginTransaction(System.Data.IsolationLevel.ReadCommitted); try { IPunishRecord.Insert(tran, PunishRecord); //通过orderNoEatCount判断,如果是对订餐未就餐的情况的话,那么就要更新状态为处理 if (PunishRecord.OrderNoEatCount > 0) { IOrderInfo.UpdateState(tran, PunishRecord.TeacherId); } ICardInfo.UpdateBalance(tran, GetCardNO(PunishRecord.TeacherId), PunishRecord.PunishCash * (-1)); tran.Commit(); conn.Close(); return PunishRecord; } catch (Exception) { tran.Rollback(); conn.Close(); return null; } } /// <summary>根据方案ID获取该方案下的信息 /// /// </summary> /// <param name="punishDataId"></param> /// <returns></returns> private DataTable GetParameter(string action) { IActionOfPunish IActionOfPunish; IActionOfPunish = DBFactory.DBFactory.CreateActionOfPunish(); int punishDataId = IActionOfPunish.GetPunisDataId(PunishRecord.PunishAction); IPunishData IPunishData; IPunishData = DBFactory.DBFactory.CreatePunishData(); return IPunishData.SelectById(punishDataId); } /// <summary>根据教师工号 /// /// </summary> /// <param name="teacherId"></param> /// <returns></returns> private string GetCardNO(string teacherId) { ITeacherInfo ITeacherInfo; ITeacherInfo = DBFactory.DBFactory.CreateTeacherInfo(); return ITeacherInfo.SelectById(teacherId).Rows[0]["cardNO"].ToString(); } } }

其中的if条件判断也就是解决了这两种处罚情况下的代码复用问题的,里面用到了事务。

到此用策略模式便比较灵活地解决了这个问题,如果将来需求再怎么变化也能够灵活应对了。