策略模式

模拟场景:

某某公司要求我们做一个商场收银系统,

提出需求:商场会不定时举办一系列的优惠活动,优惠方式暂定为:打折扣,满多少还多少(例如:满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、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。
2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。
3、 对客户隐藏具体策略(算法)的实现细节,彼此完全独立。
 

优点:

1、策略模式简化了单元测试,因为每个算法都有自己的类,可以通过自己的接口单独测试,每个算法可以保证它没有错误,修改其中任一个时也不会影响其他的算法。

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

3、策略模式有利于程序的高内聚、低偶合性,使得程序更加的灵活。

 

缺点:

基本策略模式中所用具体实现的职责由客户端对象承担,并转给策略模式的Context对象,
这本身并没有解除客户端需要选择判断的压力,而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由Context来承担,这就是最大化地减轻了客户端的职责。

 

本教程参考“大话设计模式”,作者:程杰。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值