zoukankan      html  css  js  c++  java
  • 面向对象编程思想(2)--策略模式

    定义

    策略模式
    官方定义:定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。
    个人理解:选择执行多个规则中的某个规则。

    C#实现

    需求1: 开发一个商场收银系统v1.0

    三下五除二搞定

    代码实现:

    var price = Convert.ToDouble(txtPrice.Text);//单价
    var number = Convert.ToDouble(txtNumber.Text);//数量
    var lastTotal = Convert.ToDouble(labTotal.Text);//已购买金额
    
    var money = price * number;//本次计算金额
    labTotal.Text = (lastTotal + money).ToString();
    txtContent.Text += string.Format("单价:{0},数量:{1},金额:{2}", price, number, money + "
    ");
    
    

    系统简单、方便、实用、性能好。...几个月过去,马上要国庆长假,老板为了促销决定全场8.8折。

    需求2: 加入打折功能
    因为项目工期紧,三下五除二又搞定

    var price = Convert.ToDouble(txtPrice.Text);//单价
    var number = Convert.ToDouble(txtNumber.Text);//数量
    var lastTotal = Convert.ToDouble(labTotal.Text);//已购买金额
    var discount = 0.88;//折扣(新增代码)
    
    var money = price * number * discount;//本次计算金额
    labTotal.Text = (lastTotal + money).ToString();
    txtContent.Text += string.Format("单价:{0},数量:{1},折扣:{2},实际金额:{2}", price, number, discount, money + "
    ");
    
    

    很是自豪,我开发效率就是这么高。老板也甚是高兴。
    ...转眼假期就要过去了,打折的活动也要取消了。但是,由于这次的促销效果收益还不错。老板决定继续打折活动,折扣率要成为9.8折,且只是部分商品。不打折的商品则实行满300返40,满600返100。
    不对劲啊,又的改代码。到了明年是不是又要8.8?老板的心思猜不透,但程序可以写得更灵活。所以我们要好好构思下。让系统可以选择优惠策略。

    需求3: 修改打折,并加入返现功能

    代码实现如下

    var price = Convert.ToDouble(txtPrice.Text);//单价
    var number = Convert.ToDouble(txtNumber.Text);//数量
    var lastTotal = Convert.ToDouble(labTotal.Text);//已购买金额            
    var money = price * number;//本次计算金额
    switch (cmBstrategy.Text)//下拉框
    {
    
        case "8.8折":
            money *= 0.88;
            break;
        case "9.8折":
            money *= 0.98;
            break;
        case "满300返40":
            if (money >= 300)
            {
                money -= 40;
            }
            break;
        case "满600返100":
            if (money >= 600)
            {
                money -= 100;
            }
            break;
    }
    
    labTotal.Text = (lastTotal + money).ToString();
    txtContent.Text += string.Format("单价:{0},数量:{1},促销:{2},实际金额:{3}", price, number, cmBstrategy.Text, money + "
    ");
    

    现在我们的收银员可以灵活切换优惠活动,且保留的原有的优惠策略。不过我们从代码层面考虑的话,还有多处不足。

    • switch条件分支语句难以阅读和维护。
    • 如果我们需要修改新增优惠策略的话,需在界面代码里面修改。

    根据面向对象的思想,应该封装变化。于是,我们的策略模式可以登场了。

    代码重构 使用策略模式实现以上需求

    var price = Convert.ToDouble(txtPrice.Text);//单价
    var number = Convert.ToDouble(txtNumber.Text);//数量
    var lastTotal = Convert.ToDouble(labTotal.Text);//已购买金额      
     
    var context = new Context(cmBstrategy.Text);//新增代码
    var money = context.Calculation(price, number);//新增代码
    
    labTotal.Text = (lastTotal + money).ToString();
    txtContent.Text += string.Format("单价:{0},数量:{1},促销:{2},实际金额:{3}", price, number, cmBstrategy.Text, money + "
    ");
    
    

    我们发现中间那段条件分支不见了,多了一个Context类。

    public class Context
    {
        //策略抽象类
        private AmountCalculation amountCalculation;
    
        public Context(string type)
        {
            switch (type)
            {
                case "8.8折":
                    amountCalculation = new Rebate(0.88);
                    break;
                case "9.8折":
                    amountCalculation = new Rebate(0.98);
                    break;
                case "满300返40":
                    amountCalculation = new Cashback(300, 40);
                    break;
                case "满600返100":
                    amountCalculation = new Cashback(600, 100);
                    break;
            }
        }
        //计算金额
        public double Calculation(double price, double number)
        {
            return amountCalculation.Calculation(price, number);
        }
    }
    

    里面有类Rebate折扣计算、Cashback返现计算。

    //折扣计算
    public class Rebate : AmountCalculation
    {
        private double discountRate;
        public Rebate(double discountRate)
        {
            this.discountRate = discountRate;
        }
    
        public override double Calculation(double price, double number)
        {
            return price * number * discountRate;
        }
    }
    
    // 返现
    public class Cashback : AmountCalculation
    {
        //满多少
        private double exceed;
        //返多少
        private double retreat;
        public Cashback(double exceed, double retreat)
        {
            this.exceed = exceed;
            this.retreat = retreat;
        }
        public override double Calculation(double price, double number)
        {
            var momoney = price * number;
            if (momoney >= exceed)
            {
                return momoney - retreat;
            }
            return momoney;
        }
    }
    

    看到这里,是不是明白了策略模式呢?
    如果现在老板再需要我们价格折扣或是返现的活动,相比之前需要在长段的界面逻辑代码里面修改,现在要方便得多了。
    第一、先在界面添加一个活动如加一个7.8折,然后界面代码就不用动了。
    第二、在Context类里面加一个7.8折

    switch (type)
    {
        //新增
        case "7.8折":
            amountCalculation = new Rebate(0.78);
            break;
    

    JS实现

    上面用C#实现了策略模式,接下来我们尝试使用js来实现。还是借用用上面的商场活动业务。
    js不同于传统的面向对象,无类、不需要实现抽象类。

    //策略计算
    var strategies = {
        //返现  exceed:满多少  retreat:返多少  momoney:应付金额
        cashBack: function (exceed, retreat, momoney) {
            if (momoney >= exceed) {
                return (momoney - retreat).toFixed(2);
            }
            return momoney;//返现后实付金额
        },
        //打折 discountRate:折扣率  momoney:应付金额
        rebate: function (discountRate, momoney) {
            return (discountRate * momoney).toFixed(2);//折扣后实付金额
        }
    }
    
    //上下文
    var context = {
        "7.8折": function (price, number) {
            var momoney = price * number;
            return strategies.rebate(0.78, momoney);
        },
        "9.8折": function (price, number) {
            var momoney = price * number;
            return strategies.rebate(0.98, momoney);
        },
        "满300返40": function (price, number) {
            var momoney = price * number;
            return strategies.cashBack(300, 40, momoney);
        },
        "满600返100": function (price, number) {
            var momoney = price * number;
            return strategies.cashBack(600, 100, momoney);
        }
    }
    
    //计算结果
    var calculateBonus = function (level, price, number) {
        return context[level](price, number);
    };
    
    //调用
    console.log(calculateBonus('7.8折', 12, 3));//计算
    console.log(calculateBonus('满600返100', 12, 3));//计算
    console.log(calculateBonus('满300返40', 2, 23));//计算
    console.log(calculateBonus('9.8折', 2, 33));//计算
    

    结果如下:

    相对于面向对象语言的实现要更加的清晰明了。
    那么js可以模拟面向对象的实现吗?答案是肯定的。
    首先定义返现实现类:

    //返现  exceed:满多少  retreat:返多少
    var CashBack = function (exceed, retreat) {
        this.exceed = exceed;
        this.retreat = retreat;
    };
    //计算方法
    CashBack.prototype.calculate = function (price, number) {
        var momoney = price * number;
        if (momoney >= this.exceed) {
            return (momoney - this.retreat).toFixed(2);
        }
        return momoney;//返现后实付金额
    }
    

    打折类

    //打折 discountRate:折扣率  momoney:应付金额
    var Rebate = function (discountRate) {
        this.discountRate = discountRate;
    };
    //计算方法
    Rebate.prototype.calculate = function (price, number) {
        return (price * number * this.discountRate).toFixed(2);//折扣后实付金额
    }
    

    策略上下文

    //上下文
    var Context = function (type) {
        this.type = type; 
    }
    Context.prototype.calculation = function (price, number) {
        var AmountCalculation;
        switch (this.type) {
            case "7.8折":
                AmountCalculation = new Rebate(0.78);
                break;
            case "9.8折":
                AmountCalculation = new Rebate(0.98);
                break;
            case "满300返40":
                AmountCalculation = new CashBack(300, 40);
                break;
            case "满600返100":
                AmountCalculation = new CashBack(600, 100);
                break;
        }
        return AmountCalculation.calculate(price, number);
    }
    

    调用如下:

    //调用
    var context = new Context("7.8折");  
    console.log(context.calculation(12, 3));
    var context = new Context("9.8折");  
    console.log(context.calculation(12, 3));//计算
    var context = new Context("满300返40");  
    console.log(context.calculation(300, 2));//计算
    var context = new Context("满600返100");  
    console.log(context.calculation(300, 3));//计算
    


    虽然对于js语言特性直接实现策略模式来说,面向对象的代码量比较多。可是对于我们后端人员new一个对象的使用方式应该更习惯。
     

    策略模式场景

    • 排序算法的选择冒泡排序、选择排序、快速排序、插入排序
    • 压缩算法的选择zip、rar、rar5...
    • 旅游交通工具的选择飞机、火车、汽车...
       

    总结
    策略模式通过Context上下文对具体策略进行封装,供高层直接调用而不用关系策略的具体实现。然后Context本身通过不同情况实例不同的抽象实现类(具体策略类),来执行具体策略。从而实现了具体策略的自由切换,易于新策略的扩展。
    本文已同步至索引目录:《设计模式学习》
    本文demo:https://github.com/zhaopeiym/BlogDemoCode

  • 相关阅读:
    政务公开系统专栏首页的跨站攻击漏洞
    Spring+XFire WSSecurity安全认证开发感悟
    Appfuse使用中遇到的问题及解决方案
    How to get the rowid when insert the data to Oracle database
    How to configure CVS in IntelliJ IDEA
    localhost打不开 127.0.0.1可以打开,,,或 hosts 文件不起作用的解决方法
    ASp.net中Froms验证方式
    ASP.NET 页面执行顺序详解
    【转】防止用户通过后退按钮重复提交表单ASP中的response.Buffer,Response.Expires,Response.CacheControl
    页面事件(Init,Load,PreRender)执行顺序__简单总结
  • 原文地址:https://www.cnblogs.com/zhaopei/p/strategy.html
Copyright © 2011-2022 走看看