策略模式
来源:互联网 发布:oracle数据库解锁 编辑:程序博客网 时间:2024/04/27 21:43
模拟场景:
某某公司要求我们做一个商场收银系统,
提出需求:商场会不定时举办一系列的优惠活动,优惠方式暂定为:打折扣,满多少还多少(例如:满300还100)
初步场景分析:
看到这个需求,第一感觉就会潜意识的认为“这个太简单了”。
1.商场收银系统:定义为winform的应用程序
2.活动优惠的计算,判断一下就可以了。
初步代码实现:
- /// <summary>
- /// 点击确定计算收费
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btnOk_Click(object sender, EventArgs e)
- {
- int number = Convert.ToInt32(this.txtNumber.Text);
- double price = Convert.ToDouble(this.txtPrice.Text);
- double total = 0;
- switch (this.cbxType.SelectedItem.ToString())
- {
- case "正常收费":
- total = number * price;
- break;
- case "满300返100":
- moneyCondition = 300;
- moneyReturn = 100;
- double money = Convert.ToDouble(txtPrice.Text) * Convert.ToInt32(txtNumber.Text);
- if (money >= moneyCondition)
- {
- total = money - Math.Floor(money / moneyCondition) * moneyReturn;
- }
- break;
- case "打8折":
- total = Convert.ToDouble(txtPrice.Text) * Convert.ToInt32(txtNumber.Text) * 0.8;
- break;
- }
- this.lbxList.Items.Add("单价:" + txtPrice.Text + "数量:" + txtNumber.Text + " "
- + cbxType.SelectedItem.ToString() + "合计: " + total.ToString());
- this.lblResult.Text = total.ToString();
- }
好,现在我们来分析一下上面的实现:
咋一看,感觉没什么问题,可再细分析,问题就多了:
1:显示和逻辑紧密的联系在一起。
2:完全过程式的编程,没办法复用。
3:当新需求增加的时候,还需要修改这个条件分支,不符合开放封闭原则,过多的判断不利于维护。
经过以上分析,进行初步的修改:
先来看看结构图:
工厂类:
- public class CashFactory
- {
- public static CashSuper createCashAccpet(string type)
- {
- CashSuper cs = null;
- switch (type)
- {
- case "正常收费":
- cs = new CashNormal();
- break;
- case "满300返100":
- cs = new CashReturn("300","100");
- break;
- case "打8折":
- cs = new CashRebate("0.8");
- break;
- }
- return cs;
- }
- }
运算父类:
- /// <summary>
- /// 现金收取父类,算法的抽象类
- /// </summary>
- public abstract class CashSuper
- {
- /// <summary>
- /// 抽象方法:收取现金
- /// </summary>
- /// <param name="money">原价</param>
- /// <returns>当前价</returns>
- public abstract double acceptCash(double money);
- }
折扣类:
- /// <summary>
- /// 打折收费
- /// </summary>
- public class CashRebate:CashSuper
- {
- private double moneyRebate = 1d;
- public CashRebate(string money)
- {
- this.moneyRebate = double.Parse(money);
- }
- //初始化时必需输入折扣率,如八折就是0.8
- public override double acceptCash(double money)
- {
- return money * moneyRebate;
- }
- }
返利类:
- /// <summary>
- /// 返利收费
- /// </summary>
- public class CashReturn:CashSuper
- {
- private double moneyCondition = 0.0d;
- private double moneyReturn = 0.0d;
- /// <summary>
- /// 初始化时必须要输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneyReturn为100
- /// </summary>
- /// <param name="moneyCondition"></param>
- /// <param name="moneyReturn"></param>
- public CashReturn(string moneyCondition,string moneyReturn)
- {
- this.moneyCondition = double.Parse(moneyCondition);
- this.moneyReturn = double.Parse(moneyReturn);
- }
- public override double acceptCash(double money)
- {
- double result = money;
- //若大于返利条件,则需要减去返利值
- if (money>=moneyCondition)
- {
- result = money-Math.Floor(money/moneyCondition)*moneyReturn;
- }
- return result;
- }
- }
正常收费类:
- /// <summary>
- /// 正常收费
- /// </summary>
- public class CashNormal:CashSuper
- {
- public override double acceptCash(double money)
- {
- return money;
- }
- }
好的,经过完善之后我们的代码已经出来了,下面我们再来细细分析一下这代码:
代码使用了简单工厂模式来解决了对象的创建,通过抽象实现了业务逻辑的分离,在维护性上也得到了改善。
那么这代码就没有问题了吗?答案是:有
现在假设我们要为商场添加一种优惠活动,满100积分送10点,以后积分达到一定值就可以领取奖品。
我们该怎么去做呢:现在有了工厂,我们只需要添加一个积分的算法类,
让它继承运算父类(CashSuper),在再工厂条件分支里添加一个满100积分送10点的条件分支,界面再稍稍修改就可以了。
这么想虽然是可以解决问题,但是工厂里包含了所有的收费方式,商场是可能经常性的更改打折额度,添加新的优惠方式,
如果是这样的话,我们每次维护或扩展收费方式都要去改动这个工厂,这样做就不得不让代码重新编译部署,这样的处理方式是很糟糕的。
那么我们该如何去做呢?现在我们就进入正题:其实商场的促销活动,打折活动,返利活动都是一些算法,而算法本身只是一种策略,
最重要的是这些算法是随时都可能互相替换的,这就是变化点,而封装变化点是我们面向对象的一种很重要的思维方式。
我们先来看看策略模式的结构图:
Strategy类,定义所有支持的算法和公共接口:
ConcreteStrategy封装了具体的算法或行为,继承于Strategy:
Context,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用:
客户端的实现:
下面我们就用策略模式来改善一下我们的代码:
代码结构图:
现有的代码没有CashContext,我们就添加一个CashContext的类:
其他的类不变,客户端实现:
经过策略模式完善的代码已经实现,下面我们再来分析一下我们的代码:
现在问题又来了,算法的判断又回到客户端里来了,我们该如何去解决这个问题呢?
其实我们可以让策略模式与简单工厂模式相结合,下面我们来看看怎么个实现法:
在CashContext里进行对象的构造:
客户端实现:
经过进一步的优化,我们的代码已经比较的优化了,但是一个很明显的问题也显示出来了,就是switch的条件判断分支,
当我们需要添加一种算法的时候,我们还是需要修改CashContext里的switch算法,这样的代码还是让人非常的不爽,哪还有什么方法呢?
我们可以使用反射来改善现有的代码(反射的具体原理与实现将在不久写出):
switch算法的条件判断分支我们抽取出来,用一个xml文件进行记录:
接下来我们只需要在加载事件中将xml的信息读取出来绑定就可以了:
由于我们的switch已经转移到xml中了,那么我们的CashContext类就可以简化成:
我们的具体算法实现类不变,现在我们最主要就是通过反射去实例化不同的算法对象:
总结:
代码经过多次的修改,可维护性,代耦合高内聚的特性已经展现出来了,现在我们再来分析一下上面的代码吧,上面我们通过反射去除了switch分支,
有效的把耦合降到最低,但是这样做也是有一点点代价的,反射会比较耗一点性能,所以我们做程序的时候要具体问题具体分析。
通过上面的代码我们来分析一下策略模式吧:
策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少了各种算法类与使用算法类之间的耦合。
优点:
1、策略模式简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试,每个算法可以保证它没有错误,修改其中任一个时也不会影响其他的算法。
2、策略模式封装了变化,策略模式就是用来封装算法的,但在实践中,我们发现可以用它来封装几乎任何类型的规则,
只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。
3、策略模式有利于程序的高内聚、低偶合性,使得程序更加的灵活。
缺点:
基本策略模式中所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象,
这本身并没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就是最大化地减轻了客户端的职责。
以上内容转自(尽供本人学习使用):http://blog.csdn.net/shiyuan17/article/details/9056637 。感谢博主花费时间和经历为我们准备如此好的学习资料。
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 策略模式
- 简练直观的QuickSort算法过程图示
- SQLite中的时间日期函数
- [C++/CLI编程宝典][2]什么是C++/CLI语言
- win7 局域网共享"您可能没有权限使用网络资源"的解决办法
- c# 数据导入到服务器上代码
- 策略模式
- Linux命令运行的顺序
- Unity 3D 里的碰撞检测
- JarBundler
- cocos2d-x3.0中数据类型vector,map和value的分析和挖掘
- ajaxfileupload在最新谷歌浏览器下兼容问题
- oracle row_number()
- 网站收录查询代码
- xcode内嵌字体