读《大话设计模式》——应用策略模式的"商场收银系统"(WinForm)

策略模式的结构

这个模式涉及到三个角色:

环境(Context)角色:持有一个 Strategy 类的引用。
抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。
具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。

 

 

上篇博文写的CashSuper 就是抽象策略,而正常收费 CashNormal、打折收费 CashRebate 和返利收费 CashReturn 就是三个具体策略,也就是策略模式中说的具体算法。

   附上上篇博文的部分代码

//正常消费,继承CashSuper
    class CashNormal:CashSuper
    {
        public override double acceptCash(double money)
        {
            return money;
        }
    }
View Code
//打折收费消费,继承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;
        }
    }
View Code
//返利收费
    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;
        }
    }
View Code
//现金收取父类
    abstract class CashSuper
    {
        //抽象方法:收取现金,参数为原价,返回为当前价
        public abstract double acceptCash(double money);
    }
View Code

  加入的策略模式(这里可以弃用工厂模式了)

 1 namespace ExtendDiscountOfStrategyPattern
 2 {
 3     class CashContext
 4     {
 5         //声明一个现金收费父类对象
 6         private CashSuper cs;
 7 
 8         //设置策略行为,参数为具体的现金收费子类(正常,打折或返利)
 9         public void setBehavior(CashSuper csuper)
10         {
11             this.cs = csuper;
12         }
13 
14         //得到现金促销计算结果(利用了多态机制,不同的策略行为导致不同的结果)
15         public double GetResult(double money)
16         {
17             return cs.acceptCash(money);
18         }
19     }
20 }

  但是程序还是少不了switch...case语句,

  核心代码(v1.3)

 1 //声明一个double变量total来计算总计
 2         double total = 0.0d;
 3         private void btnConfirm_Click(object sender, EventArgs e)
 4         {
 5             //声明一个double变量totalPrices
 6             double totalPrices = 0d;
 7            //策略模式
 8             CashContext cc = new CashContext();
 9             switch (cbxType.SelectedItem.ToString())
10             {
11                 case "正常消费":
12                     cc.setBehavior(new CashNormal());
13                     break;
14                 case "满300返100":
15                     cc.setBehavior(new CashReturn("300", "100"));
16                     break;
17                 case "打8折":
18                     cc.setBehavior(new CashRebate("0.8"));
19                     break;
20                 case "打7折":
21                     cc.setBehavior(new CashRebate("0.7"));
22                     break;
23                 case "打5折":
24                     cc.setBehavior(new CashRebate("0.5"));
25                     break;
26             }
27             totalPrices = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));
28             //将每个商品合计计入总计
29             total = total + totalPrices;
30             //在列表框中显示信息
31             lbxList.Items.Add("单价:" + txtPrice.Text + "  数量:" + txtNum.Text + "  合计:" + totalPrices.ToString());
32             //在lblTotalShow标签上显示总计数
33             lblTotalShow.Text = total.ToString();
34         }

 

  最初的策略模式是有缺点的,客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户
端知道所有的算法或行为的情况最初的策略模式是有缺点的,客户端必须知道所有的策略类,并自行决定使用哪一个策略类。这就意味着客户端必须理解这些算法的区别,以便适时选择恰当的算法类。换言之,策略模式只适用于客户端知道所有的算法或行为的情况。

 

  去掉switch...case语句!!!——(本案例采用简单的.net技术:反射

  

  关键的操作代码为:Assembly.Load(" 程序集名称").CreateInstance(" 名称空间.类名称");

 

  

 

  

  客户端代码(v1.4)

 

  1 using System;
  2 using System.Collections.Generic;
  3 using System.ComponentModel;
  4 using System.Data;
  5 using System.Drawing;
  6 using System.Linq;
  7 using System.Text;
  8 using System.Threading.Tasks;
  9 using System.Windows.Forms;
 10 //add
 11 using CCWin;
 12 using System.Data.SqlClient;
 13 
 14 namespace ExtendDiscountOfStrategyPatternWithReflection
 15 {
 16      using System.Reflection;
 17 
 18     public partial class frmMain :Skin_Metro
 19     {
 20 
 21         DataSet ds;             //用于存放配置文件信息
 22 
 23         public frmMain()
 24         {
 25             InitializeComponent();
 26         }
 27         
 28         //声明一个double变量total来计算总计
 29         double total = 0.0d;
 30         private void btnConfirm_Click(object sender, EventArgs e)
 31         {
 32             //声明一个double变量totalPrices
 33             double totalPrices = 0d;
 34            //策略模式
 35             CashContext cc = new CashContext();
 36             //根据用户的选项,查询用户选择项的相关行
 37             DataRow dr = ((DataRow[])ds.Tables[0].Select("name='" + cbxType.SelectedItem.ToString() + "'"))[0];
 38             //声明一个参数的对象数组
 39             object[] args = null;
 40             //若有参数,则将其分割成字符串数组,用于实例化时所用的参数
 41             if (dr["para"].ToString() != "")
 42             {
 43                 args = dr["para"].ToString().Split(',');
 44             }
 45             //通过反射实例化出相应的算法对象
 46             cc.setBehavior((CashSuper)Assembly.Load("ExtendDiscountOfStrategyPatternWithReflection").
 47                 CreateInstance("ExtendDiscountOfStrategyPatternWithReflection." + dr["class"].ToString(), false,
 48                 BindingFlags.Default, null, args, null, null));
 49 
 50             totalPrices = cc.GetResult(Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text));
 51             //将每个商品合计计入总计
 52             total = total + totalPrices;
 53             //在列表框中显示信息
 54             lbxList.Items.Add("单价:" + txtPrice.Text + "  数量:" + txtNum.Text + "  合计:" + totalPrices.ToString());
 55             //在lblTotalShow标签上显示总计数
 56             lblTotalShow.Text = total.ToString();
 57         }
 58 
 59         private void btnReset_Click(object sender, EventArgs e)
 60         {
 61             total = 0.0;
 62             txtPrice.Text = "";
 63             txtNum.Text = "";
 64             lblTotalShow.Text = "";
 65             lbxList.Items.Clear();
 66             cbxType.SelectedIndex = 0;
 67         }
 68 
 69         private void txtNum_KeyPress(object sender, KeyPressEventArgs e)
 70         {
 71             //数字0~9所对应的keychar为48~57
 72             e.Handled = true;
 73             //输入0-9
 74             if ((e.KeyChar >= 47 && e.KeyChar <= 58) || e.KeyChar == 8)
 75             {
 76                 e.Handled = false;
 77             } 
 78         }
 79 
 80         private void txtPrice_KeyPress(object sender, KeyPressEventArgs e)
 81         {
 82             //数字0~9所对应的keychar为48~57
 83             e.Handled = true;
 84             //输入0-9
 85             if ((e.KeyChar >= 47 && e.KeyChar <= 58) || (e.KeyChar == 8 || e.KeyChar==46))
 86             {
 87                 e.Handled = false;
 88             } 
 89         }
 90 
 91         private void frmMain_Load(object sender, EventArgs e)
 92         {
 93             //读取配置文件
 94             ds = new DataSet();
 95             ds.ReadXml(Application.StartupPath + "\\CashAcceptType.xml");
 96             //将读取到的记录绑定到下拉列表框中
 97             foreach(DataRowView dr in ds.Tables[0].DefaultView)
 98             {
 99                 cbxType.Items.Add(dr["name"].ToString());
100             }
101 
102             //要下拉选择框在加载的时候,就选择索引为0的元素"正常消费"
103             cbxType.SelectedIndex = 0;
104         }
105     }
106 }

  通过程序去读XML的配置文件,来生成这个下拉列表框,然后再根据用户的选择,通过反射实时的实例化出相应的算法对象,最终利用策略模式计算最终的结果。

  XML文件——CashAcceptType.xml代码如下

  

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <CashAcceptType>
 3   <type>
 4     <name>正常消费</name>
 5     <class>CashNormal</class>
 6     <para></para>
 7   </type>
 8   <type>
 9     <name>满300返100</name>
10     <class>CashReturn</class>
11     <para>300,100</para>
12   </type>
13   <type>
14     <name>满200返50</name>
15     <class>CashReturn</class>
16     <para>200,50</para>
17   </type>
18   <type>
19     <name>打8折</name>
20     <class>CashRebate</class>
21     <para>0.8</para>
22   </type>
23   <type>
24     <name>打7折</name>
25     <class>CashRebate</class>
26     <para>0.7</para>
27   </type>
28   <type>
29     <name>打5折</name>
30     <class>CashRebate</class>
31     <para>0.5</para>
32   </type>
33 </CashAcceptType>

  现在无论需求是什么,用现在的程序,只需要改改XML文件就全部摆平了。比如现在老板觉得现在满300送100太多了,要改成送80,我只需要去XML文件里改就行了。

注:如要添加新的算法,那么该算法类继承CashSuper,再去改一下XML文件就可以了。

posted on 2020-07-11 18:04  笠侹凯树  阅读(184)  评论(0编辑  收藏

导航