策略模式

应用场景

一个商场收银软件,营业员根据客户所购买的商品的单价和数量,向客户收费

用两个文本框来输入单价和数量,一个确定按钮来算出每种商品的费用,用个列表框来记录商品的清单,一个标签来记录总计,一个重置按钮来重新开始。

  1. double total = 0.0d;
  2. private void btn0k_Click(object sender, EventArgs e)
  3. {
  4. double totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text);
  5. total = total + totalPrices;
  6. IbxList.Items.Add("单价:" +txtPrice.Text + "数量:" txtNum.Text + "合计:"+ totalPrices.ToString());
  7. IblResult.Text = total.ToString();
  8. }

比如遇到节假日 增加打折

  1. double total = 0.0d;
  2. private void Form_Load(object sender, EventArgs e)
  3. {
  4. cbxType.Items.AddRange(new object[] {"正常收费","打八折","打五折"});
  5. cbxType.SlectedIndex = 0;
  6. }
  7. private void btn0k_Click(object sender, EventArgs e)
  8. {
  9. double totalPrices = 0.0d;
  10. switch(cbxType.Selectedindex)
  11. {
  12. case 0:
  13. totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text);
  14. case 1:
  15. totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text)*0.8;
  16. case 2:
  17. totalPrices = Convert.ToDouble(txtPrice.Text) * Convert.ToDouble(txtNum.Text)*0.5;
  18. }
  19. total = total + totalPrices;
  20. IbxList.Items.Add("单价:" +txtPrice.Text + "数量:" txtNum.Text + "合计:"+ totalPrices.ToString());
  21. IblResult.Text = total.ToString();
  22. }

简单工厂实现

面对对象的编程,并不是类越多越好,类的划分是为了封装,但是分装的基础是抽象,具有相同属性和功能的对象的抽象集合才是类。打一折和打九折只是形式的不同,抽象分析出来,所有的打折算法都是一样的,所以打折算法应该是一个类。

  1. //现金收费抽象类
  2. abstract class CashSuper
  3. {
  4. public abstract double acceptCash(double money);
  5. }
  6. //正常收费子类
  7. class CashNormal: CashSuper
  8. {
  9. public override double acceptCash(double money)
  10. {
  11. return money;
  12. }
  13. }
  14. //打折收费子类
  15. class CashRebate: CashSuper
  16. {
  17. private double moneyRebate = 1d;
  18. public Cash Rebebate(string moneyRebate)
  19. {
  20. this.moneyRebate = double.Parse(moneyRebate);
  21. }
  22. publci override double acceptCash(double money)
  23. {
  24. return money * moneyRebate;
  25. }
  26. }
  27. //返利收费子类
  28. class cashReturn: CashSuper
  29. {
  30. private double moneyCondition = 0.0d;
  31. private double moneyreturn = 0.0d;
  32. public CashReturn(string moneyCondition, string moneyReturn)
  33. {
  34. this.moneyCondition = double.Parse(moneyCondtion);
  35. this.moneyReturn = double.Parse(moneyReturn);
  36. }
  37. public override double acceptCash(double money)
  38. {
  39. double result = money;
  40. if(money >= moneyCondition)
  41. {
  42. result = money - Math.Floor(money / moneyCondition) * moneyReturn;
  43. }
  44. return result;
  45. }
  46. }
  47. //现金收费工厂类
  48. class CashFactory
  49. {
  50. public static CashSuper createCashAccept(string type)
  51. {
  52. CashSuper cs = null;
  53. switch (type)
  54. {
  55. case:"正常收费":
  56. cs = new CashNormal();
  57. break;
  58. case:"满300返100":
  59. CashReturn cr1 = new CashReturn("300","100");
  60. cs = cr1;
  61. break;
  62. case:"打8折":
  63. CashRebate cr2 = new CashRebate("0,8");
  64. cs = cr2;
  65. break;
  66. }
  67. return cs;
  68. }
  69. }
  70. //客户端程序主要部分
  71. double total = 0.0d;
  72. private void btn0k_clik(object sneder, EventArgs e)
  73. {
  74. cashSuper csuper = CashFactory.createCashAccept(cbxType.SelectedItem.ToString());
  75. double totalPrices = 0d;
  76. totalPrices = csuper.acceptCash(Convert.ToDouble(txtPrice.txt)*Convert.ToDouble(txtNum.Text));
  77. total = total + totalPrices;
  78. IbxList.Items.Add("单价:" +txtPrice.Text + "数量:" txtNum.Text + "合计:"+ totalPrices.ToString());
  79. IblResult.Text = total.ToString();
  80. }

简单工厂模式虽然能解决这个问题,但是这个模式知识解决对象的创建问题,而且由于工厂本身包括了所有的收费方式,商场是可能经常性地更改打折额度和返利额度,每次维护或扩展收费方式都要改动这个工厂,以致代码需重新编译部署,这真的是很糟糕的处理方式,所以用它不是做好的办法,。面对算法的时常变动应该有更好的办法。

策略模式

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

  1. //CashContext类
  2. class CashContext
  3. {
  4. private CashSuper cs;
  5. public CashContext(CashSuper csuper)
  6. {
  7. this.cs = csuper;
  8. }
  9. public double GetResult(double money)
  10. {
  11. return cs.accptCash(money);
  12. }
  13. }
  14. //客户端主要代码
  15. double total = 0.0d;
  16. private void btn0k_Click(object sender, EventArgs e)
  17. {
  18. CashContext cc = null;
  19. switch (cbxType.selectedItem.ToString())
  20. {
  21. case: "正常收费":
  22. cc = new CashContext(new CashNormal());
  23. break;
  24. case: "满300返100":
  25. cc = new CashContext(new CashReturn ("300","100"));
  26. break;
  27. case: "打8折":
  28. cc = new CashContext(new CashRebate("0.8"));
  29. break;
  30. }
  31. double totalPrices = 0d;
  32. totalPrices = cc.GetResult(convert.ToDouble(txtPrice.Text)*convert.ToDouble(txtNum.Text));
  33. total = total + totalPrices;
  34. IbxList.Items.Add("单价:" +txtPrice.Text + "数量:" txtNum.Text + "合计:"+ totalPrices.ToString());
  35. IblResult.Text = total.ToString();
  36. }

虽然策略模式写出来了,但是不应该让客户端去判断用哪一个算法。

策略与简单工厂结合

  1. class CashContext
  2. {
  3. CashSuper cs = null;
  4. public CashContext(String type)
  5. {
  6. switch(type)
  7. {
  8. case "正常收费":
  9. CashNormal cs0 = new CashNormal();
  10. cs = cs0;
  11. break;
  12. case "满300返100":
  13. CashReturn cr1 = new CashReturn("300","100");
  14. cs = cr1;
  15. break;
  16. case "打8折"
  17. CashReturn cr2 = new CashRebate("0.8");
  18. cs = cr2;
  19. break;
  20. }
  21. public double GetResult(double money)
  22. {
  23. return cs.acceptCash(money);
  24. }
  25. }
  26. }
  27. //客户端代码
  28. double total = 0.0d;
  29. private void btn0k_Click(object sender, EvnetArgs e)
  30. {
  31. CashContext csuper = new CashContext(cbxType.slectedItem.ToString());
  32. double totalPrices = 0d;
  33. totalPrices = csuper.GetResult(Convert.ToDouble(txtPrice.Text)*Convert.ToDouble(txtNum.Text));
  34. total = total + totalPrices;
  35. IbxList.Items.Add("单价:" +txtPrice.Text + "数量:" txtNum.Text + "合计:"+ totalPrices.ToString());
  36. IblResult.Text = total.ToString();
  37. }
  38. //简单工厂模式的用法
  39. CashSuper csuper = CashFactory.CreateCashAccept(cbxType.SelectedItem.ToString());
  40. =csuper.GetResult;
  41. //策略模式与简单工厂结合的用法
  42. CashContext csuper = new (CashContext(cbxType.SelectedItem.ToString()));
  43. =csuper.GetResult;

简单工厂模式让客户端认识两个类, CashSuper和CashFactory,而策略模式与简单工厂结合的用法,客户端只需要认识一个类CashContext就可以了。耦合更加降低。

策略模式解析

策略模式是一种定义一系列算法的方法,从概念上来看,所有这些算法完成的都是相通的工作,只是实现不同,它可以以相同的方式调用所有的算法,减少各种算法类和使用算法类之间的耦合。
策略模式就是用来封装算法的,但是实践中,我们发现可以用它来分装几乎任何类型的规则,只要在分析过程中听到需要在不同的时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。