zoukankan      html  css  js  c++  java
  • 设计模式之策略模式

    策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。

    举个例子:
    出场人物:小菜(菜鸟级程序员),大鸟(骨灰级程序员)
      要求:做一个商场收银软件,营业员根据客户所购买的商品的单价和数量,向客户收费。
    商场收银系统v1.0运行截图以及关键代码
     public partial class Form1 : Form    {      
    public Form1() { InitializeComponent(); } //声明一个double变量total来计算总计 double total = 0.0d; private void btnOk_Click(object sender, EventArgs e) { //声明一个double变量totalPrices来计算每个商品的单价(txtPrice)*数量(txtNum)后的合计
    double totalPrices=Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text); total = total + totalPrices;//将每个商品合计计入总计
    lbxList.Items.Add("单价:"+txtPrice.Text+" 数量:"+txtNum.Text+" 合计:"+totalPrices.ToString());//在列表框中显示信息
    lblResult.Text = total.ToString();//在lblResult标签上显示总计数
    }
    }
    }

    现在问题来了:

    大鸟:昨天妈妈节商场的所有商品打八折,那么上面的代码需要怎样修改呢?

    小菜:上面的totalPrices乘以0.8不就可以了,没什么大不了的。

    大鸟:但是过了妈妈节商场老板决定恢复原来的价格,那你是不是又要再改回来?不过再过不久也就六一儿童节了,商场要是打算打七折呢,你怎么办?

    小菜:这样呐,我就加一个combox,里面给出打折选项就可以啦。

    大鸟笑而不语。

    商场收银系统v1.1运行截图以及关键代码

    double total = 0.0d;
      
            private void Form1_Load(object sender, EventArgs e)
            {
                cbxType.Items.AddRange(new object[] {"正常收费","打八折","打七折","打五折"});//在combox中添加下拉选项
                cbxType.SelectedIndex = 0;
            }
    
            private void btnOk_Click(object sender, EventArgs e)
            {
                double totalPrices=0d;
                switch(cbxType.SelectedIndex)
                {
                    case 0:
                        totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text);
                        break;
                    case 1:
                        totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text) * 0.8;
                        break;
                    case 2:
                        totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text) * 0.7;
                        break;
                    case 3:
                        totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text) * 0.5;
                        break;
    
                }
                total = total + totalPrices;
                lbxList.Items.Add("单价:" + txtPrice.Text + " 数量:" + txtNum.Text 
                    + " "+cbxType.SelectedItem+ " 合计:" + totalPrices.ToString());
                lblResult.Text = total.ToString();
         }

    小菜:这样就可以了吧。

    大鸟:比之前灵活了些,但是问题也不少:首先Convert.ToDouble()在这里就写了8遍,另外商场决定加大活动力度,满300返100这样的促销算法,你觉得该怎么办?

    小菜:那意思就是满300返100,700的话就返200了?写函数就可以了吧?

    大鸟:看来之前的简单工厂模式是白学了。

    小菜:哦哦哦,这样子呐。那就先写一个父类,在继承它实现多个打折和返利的子类,使用多态,是这样子吧?

    大鸟:这样你准备写多少个子类?

    小菜:根据商店需求呀,比如9折,8折,6折,满300返100,满400返180......要多少写多少O(∩_∩)O~~

    大鸟:小菜没有认真考虑这些促销活动之间的相同点和不同点呐

    小菜:(⊙o⊙)哦,好像是有相同的地方,可以分为打折促销,返利促销,正常收费三个模式。这样可以写一个父类,包含抽象方法:收钱(),然后写三个子类(正常收费,返利收费,打折收费)分别实现这个收钱的函数,然后使用简单工厂模式,创建一个收费工厂类,在里面进行收费的选择,创建不同的收费实例。

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

    商场收银系统v1.1运行截图以及关键代码

    代码结构图:

    现金收费子类:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace 商场管理软件
    {
    //现金收取父类
    abstract class CashSuper
    {
        //抽象方法:收取现金,参数为原价,返回为当前价
        public abstract double acceptCash(double money);
    }
    
    }

    正常收费子类:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace 商场管理软件
    {
        //正常收费,继承CashSuper
        class CashNormal : CashSuper
        {
            public override double acceptCash(double money)
            {
                return money;
            }
        }
    
    }

    打折收费子类:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace 商场管理软件
    {
        //打折收费,继承CashSuper
        class CashRebate : CashSuper
        {
            private double moneyRebate = 1d;
            //初始化时,必需要输入折扣率,如八折,就是0.8
            public CashRebate(string moneyRebate)
            {
                this.moneyRebate = double.Parse(moneyRebate);
            }
    
            public override double acceptCash(double money)
            {
                return money * moneyRebate;
            }
        }
    }

    返利收费子类:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace 商场管理软件
    {
        //返利收费,继承CashSuper
        class CashReturn : CashSuper
        {
            private double moneyCondition = 0.0d;
            private double moneyReturn = 0.0d;
            //初始化时必须要输入返利条件和返利值,比如满300返100,则moneyCondition为300,moneyReturn为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;
            }
        }
    }

    现金收费工厂类:

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace 商场管理软件
    {
    //现金收取工厂
    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;
        }
    }
    }

    客户端程序主要部分:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Text;
    using System.Windows.Forms;
    
    namespace 商场管理软件
    {
        public partial class Form1 : Form
        {
            public Form1()
            {
                InitializeComponent();
            }
    
            //客户端窗体程序(主要部分)
            double total = 0.0d;
            private void btnOk_Click(object sender, EventArgs e)
            {
                //利用简单工厂模式根据下拉选择框,生成相应的对象
                CashSuper csuper = CashFactory.createCashAccept(cbxType.SelectedItem.ToString());
                double totalPrices = 0d;
                //通过多态,可以得到收取费用的结果
                totalPrices = csuper.acceptCash(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));
                total = total + totalPrices;
                lbxList.Items.Add("单价:" + txtPrice.Text + " 数量:" + txtNum.Text + " "
                    + cbxType.SelectedItem + " 合计:" + totalPrices.ToString());
                lblResult.Text = total.ToString();
            }
    
            private void btnClear_Click(object sender, EventArgs e)
            {
                total = 0d;
                txtPrice.Text = "0.00";
                txtNum.Text = "0";
                lbxList.Items.Clear();
                lblResult.Text = "0.00";
            }
        }
    }

     

    小菜:大鸟,这样不管怎样修改,我都可以简单处理了。

    大鸟:那我要实现打五折和满500返200的返利活动,你怎么修改?

    小菜:在现金工厂中添加打五折和满500返200的case语句,然后在下拉选择框中添加两个选项就可以了。

    大鸟:现金工厂?应该是收费对象生成工厂才准确。但是如果促销修改为满100积分加10,当积分达到一定时候就可以领取奖品怎么做?

    小菜:有了工厂,何难?添加一个积分算法,构造方法里面有两个参数:条件和返点,继承CashSuper,然后在现金工厂,哦,不对,是收费对象生成工厂里面增加满100积分加10的分支条件,然后在下拉选择框里面添加这个选项就可以了。

    大鸟:但是在以后的学习中我们会发现这样的设计模式并不是最好的选择,因为每次维护或者扩展都要修改工厂这个类,以至于代码需要重新编译部署,这样的处理很糟糕,自己去研究一下看看那个设计模式可以用在其中。

    小菜(陷入沉思......)

     次日

    小菜:大鸟我知道该使用哪种设计模式了,选择策略模式就很好地解决了这个问题。

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

    大鸟:你说说看。

    小菜:这里是我画的策略模式结构图

     

    using System;
    using System.Collections.Generic;
    using System.Text;
    
    namespace 策略模式
    {
        class Program
        {
            static void Main(string[] args)
            {
                Context context;
    
                context = new Context(new ConcreteStrategyA());
                context.ContextInterface();
    
                context = new Context(new ConcreteStrategyB());
                context.ContextInterface();
    
                context = new Context(new ConcreteStrategyC());
                context.ContextInterface();
    
                Console.Read();
            }
        }
    
        //抽象算法类
        abstract class Strategy
        {
            //算法方法
            public abstract void AlgorithmInterface();
        }
        //具体算法A
        class ConcreteStrategyA : Strategy
        {
            //算法A实现方法
            public override void AlgorithmInterface()
            {
                Console.WriteLine("算法A实现");
            }
        }
        //具体算法B
        class ConcreteStrategyB : Strategy
        {
            //算法B实现方法
            public override void AlgorithmInterface()
            {
                Console.WriteLine("算法B实现");
            }
        }
        //具体算法C
        class ConcreteStrategyC : Strategy
        {
            //算法C实现方法
            public override void AlgorithmInterface()
            {
                Console.WriteLine("算法C实现");
            }
        }
        //上下文
        class Context
        {
            Strategy strategy;
    
            public Context(Strategy strategy)
            {
                this.strategy = strategy;
            }
            //上下文接口
            public void ContextInterface()
            {
                strategy.AlgorithmInterface();
            }
        }
    
    }
  • 相关阅读:
    Bellman-Ford(BF)和Floyd算法
    Dijkstra实现最短路径
    【图论】连通分量个数(并查集)
    【模拟】n a^o7 !
    【图论】最小生成树
    【搜索DFS】图的深度遍历(dfs)
    【搜索BFS】poj3278--Catch That Cow(bfs)
    【图论】判断给定图是否存在合法拓扑序列
    二叉排序树
    【树】判断给定森林中有多少棵树(简单做法)
  • 原文地址:https://www.cnblogs.com/zpfbuaa/p/5475908.html
Copyright © 2011-2022 走看看