第 2 章 商场促销——策略模式 (节选自:《大话设计模式》)

    面向对象的编程并不是类越多越好,类的划分是为了封装,但分类的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。

2.3 简单工厂实现

   现金收费抽象类:

    public abstract class CashSuper
    {
        //现金收取超类的抽象方法,收取现金,参数为原价,返回当前价
        public abstract double AcceptCash(double money);
    }

       正常收费子类:

        class CashNormal : CashSuper
        {
            public override double AcceptCash(double money)
            {
                return money;
            }
        }

       打折收费子类:

        class CashRebate : CashSuper
        {
            private double moneyRebate = 1d;
         //打折收费,初始化时,必须要输入折扣率,如打8折,就是0.8     
public CashRebate(string moneyRebate)     {     this.moneyRebate = double.Parse(moneyRebate);     }     public override double AcceptCash(double money)     {     return money * moneyRebate;     }     }

       返利收费子类:

        class CashReturn :CashSuper
        {
            private double moneyCondition = 0.0d;
            private double moneyReturn = 0.0d;
            //返利收费,初始化时必须要输入返利条件和返利值,比如满300返100
            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;
            }
        }

   现金收费工厂类:(收费对象生成工厂)

    class CashFactory
    {
        public static CashSuper CreateCashAccept(string type)
        {
            CashSuper cs = null;
            switch(type)
            {
                case "正常收费":        //根据条件返回对应的对象
                    cs = new CashNormal();
                    break;
                case "满300返100":
                    CashReturn cr1 = new CashReturn("300", "100");
                    cs = cr1;
                    break;
                case "打8折":
                    CashRebate cr2 = new CashRebate("0.8");
                    cs = cr2;
                    break;
            }
            return cs;
        }
    }

   客户端程序关键代码:

            CashSuper csuper = CashFactory.CreateCashAccept("正常收费");

    简单工厂模式只是解决对象的创建问题,虽然工厂可能已经包括了所有的收费方式,但商场可能经常性地更改打折额度和返利额度,每次维护或扩展收费方式都要

  改动这个工厂,以致代码需要重新编译部署,这真的是很糟糕的处理方式,所以用它部署最好的办法。

    面对算法的时常变动,还有更适合的设计模式。(策略模式)

2.4 策略模式

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

    商场收银时如何促销,用打折还是返利,其实都是一些算法,用工厂来生成算法对象,这没有错但算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,

  这就是变化点,而封装变化点是我们面向对象的一种重要的思维方式

  抽象算法类:(定义所有支持的算法的公共接口)

        abstract class Strategy
        {
            //算法方法
            public abstract void AlgorithmInterface();
        }

     具体算法A:(封装了具体的算法或行为,继承于抽象算法类)

            class ConcreteStrategyA : Strategy
            {
                //算法A实现方法
                public override void AlgorithmInterface()
                {
                    Console.WriteLine("算法A实现");
                }
            }

     具体算法B:

            class ConcreteStrategyB :Strategy
            {
                public override void AlgorithmInterface()
                {
                    Console.WriteLine("算法B实现");
                }
            }

  上下文类:(用一个具体的算法对象来配置,维护一个对 Strategy 对象的引用)

        class Content
        {
            Strategy strategy;
            public Content(Strategy strategy)       //通过构造方法,传入具体的策略对象
            {
                this.strategy = strategy;
            }

            //上下文接口,根据具体的策略对象,调用其算法的方法
            public void ContentInterface()
            {
                strategy.AlgorithmInterface();
            }
        }

2.5 策略模式实现

     现金收费抽象类和三个具体的策略类不需要更改。

  CashContent 类:

        class CashContent
        {
            private CashSuper cs;       //声明一个 CashSuper对象
            public CashContent(CashSuper csuper)            //通过构造方法,传入具体的收费策略
            {
                cs = csuper;
            }

            public double GetResult(double money)
            {
                return cs.AcceptCash(money);        //根据收费策略的不同,获得计算结果
            }
        }

    根据目前的代码,仍需要在客户端判断用哪一种算法(即 switch 语句仍需要写在客户端代码里),应该想一个办法把判断从客户端程序中移走。

    放在客户端和放在其他类的区别在于:当在多处调用时维护(修改)起来更方便,同时也更容易扩展和移植到其他程序上去

    简单工厂模式与策略模式的对比:

       在简单工厂模式中,工厂类中只有一个返回需要实例化的对象的方法

       在策略模式中,上下文类包含三部分:

          第一部分是一个类型为抽象算法类的字段(修饰符可以为默认的友好型),

          第二部分是上下文类的构造函数,初始化时需要传入具体的策略对象

          第三部分是上下文接口(一个根据具体的策略对象,调用其算法的方法)。

2.6 策略与简单工厂结合

  改造后的 CashContent:

        class CashContent
        {
            private CashSuper cs;       //声明一个 CashSuper 对象
            public CashContent(string type)             //注意参数不是具体的收费策略对象,而是一个字符串,表示收费类型
            {
                switch (type)
                {
                    case "正常收费":
                        CashNormal cs0 = new CashNormal();
                        cs = cs0;
                        break;
                    case "满300返100":
                        CashReturn cr1 = new CashReturn("300", "100");
                        cs = cr1;
                        break;
                    case "打8折":
                        CashRebate cr2 = new CashRebate("0.8");
                        cs = cr2;           //将实例化具体策略的过程由客户端转移到 Content 类中,这是简单工厂的应用
                        break;
                }
            }

            public double GetResult(double money)
            {
                return cs.AcceptCash(money);        //根据收费策略的不同,获得计算结果
            }
        }

  使用改造后的上下文类,客户端代码变得简单明了了:

    // 简单工厂模式的用法:(客户端需要认识两个类,CashSuper 和 CashFactory)

            CashSuper csuper = CashFactory.CreateCashAccept("正常收费");

    // 策略模式与简单工厂结合的用法:(客户端只需要认识一个类 CashContent 就可以了,耦合度更低了)—— 只要提到“客户端”,其实就是调用的地方

            CashContent csuper = new CashContent("正常收费");

2.7 策略模式解析

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

    在基本的策略模式中,选择所用具体实现的职责由客户端对象承担,并转给策略模式的 Content 对象(CashContent cc = null;…… cc = new CashContent(new CashNormal());)。这本身并没有解除客户端需要选择判断的压力,

  而策略模式与简单工厂模式结合后,选择具体实现的职责也可以由 Content 来承担,这就最大化地减轻了客户端的职责。(换句话说,当在多处进行调用时就变得更加简单了)

    这已经比起初的策略模式好用了,但它还不够完美,因为在 CashContent 里还是用到了 switch,也就是说,如果我们需要增加一种算法,比如“满200送50”,你就必须要更改 CashContent 中的 switch 代码,

  这总还是让人很不爽。更好的办法就是使用反射技术来解决

原文地址:https://www.cnblogs.com/zhangchaoran/p/8342712.html