c#设计模式之策略模式

  策略模式的要点是封装一组算法,每个算法为独立的类,可以相互替代,因为它们有相似的行为。下面看一个具体的例子:

 

这是一个彩票网站,采用策略模式的真实案例。我们最终要计算不同彩种,不同方案,不同玩法的中奖率。3码:所有的3位数字,每位数字不能重复(022就不行),且按从小到大顺序排列。范围在012-789之间,4码:4位数字,推而广之,所谓几码,就是几位数字。因为时时彩是五位数字,所以有个十百千万

2期方案,就是把最近100期,按2期划分为一组,(100,99),(98,97)等等,总共有50组。同理,3期方案,就是把最近100期,每3期划分为一组。

那如何计算中奖率呢?以上图为模型,我们现在选择了新疆时时彩的最近100期,2期方案,考察个位和十位,计算3码、4码、5码、6码的中奖率。比如说3码中的 0,1,2的中奖率的计算方法,如下图所示:

这是从1期到10期的中奖号码,末尾为12的有3组,总共5组,那么中奖率为 3/5 。同理,如果100期,里面中奖的组有N个,总共有50组,所以中奖率为 N/50。

我们先定义一个抽象的玩法类:

 1  public abstract class Play
 2     {
 3         /// <summary>
 4         /// 中奖号码
 5         /// </summary>
 6         public List<string> PrizeNums { set; get; }
 7 
 8         /// <summary>
 9         /// 几期方案
10         /// </summary>
11         public int PeriodScheme{ set; get; }
12 
13         /// <summary>
14         /// 选择的位数,比如选中个位和十位
15         /// </summary>
16         public string ChoosePositions { set; get; }
17 
18         public Play(List<string> prizeNums,int period,string choosePositions)
19         {
20             this.PrizeNums = prizeNums;
21             this.PeriodScheme = period;
22             this.ChoosePositions = choosePositions;
23         }
24         public abstract Dictionary<string,double> ComputePrizeRate();
25     }

接下来是具体的实现类:

 1 public class ThreeCodePlay : Play
 2     {
 3         public ThreeCodePlay(List<string> prizeNums, int period, string choosePositions)
 4             : base(prizeNums, period, choosePositions)
 5         {
 6 
 7         }
 8 
 9         public override Dictionary<string, double> ComputePrizeRate()
10         {
11             var rates = new Dictionary<string, double>();
12             //获取三码的所有数 (0,1,2)-(7,8,9)
13             //计算中奖率
14             return rates;
15         }
16     }

 

还有4码,五码,一直到九码,都需要实现。这里我就不赘述了。有兴趣的读者可自行实现。

接下来,我们需要一个决策类,这个类直接负责和客户端打交道。

 1  public class PlayContext
 2     {
 3         private Play play;
 4 
 5         public PlayContext(Play play)
 6         {
 7             this.play = play;
 8         }
 9         public Dictionary<string, double> GetPrizeRate()
10         {
11             return play.ComputePrizeRate();
12         }
13     }

客户端调用:

1  public class Client
2     {
3         public Dictionary<string, double> GetPrizeRate()
4         {
5             Play play = new ThreeCodePlay(new List<string>() { "01005" }, 3, "00011");
6             PlayContext context = new PlayContext(play);
7             return context.GetPrizeRate();
8         }
9     }

     有读者读到这儿,觉得这个 Playcontext类是不是多余的呢?我客户端既然知道具体的玩法类,我可以直接调用玩法类计算中奖率,何必还要通过你PlayContext呢?再说了,你PlayContext类什么都没做啊。其实策略模式当中的算法就和我们公司的所有普通员工一样,直接上级领导类似PlayContext。其实PlayContext对play对象做了一个封装,封装使客户端和play类算法调用解耦,试想下,如果客户端直接调用play对象的计算方法,那么,如果play对象的算法名改变,那么我们是不是得改客户端的代码了。一个客户端好办,如果是很多个客户端,那么就是灾难。另外PlayContext类还可以添加其它方法,比如,客户端想要知道,3码的数一共是多少个,等等。

    有读者可能觉得,你这个策略模式,明明需要客户端知道有哪些玩法,不等于还是把具体的玩法类暴露出来了。是的,为了进一步隔离客户端和玩法类,把构造玩法对象,放到PlayContext中,PlayContext根据条件,自行构造玩法对象。这不就成了简单工厂模式了吗?问题来了,简单工厂模式和策略模式到底有什么不同呢?

    1、关注点不同:工厂模式封装的是复杂对象的创建,而策略模式封装的是一个对象的多种行为。PlayContex如果作为工厂,肯定返回一个play对象,如果作为策略,肯定执行一个Play对象的行为。

    2、策略模式可以使客户端避免直接接触算法的一些细节,工厂模式可以使客户端不必关心对象的构造过程。

再举个额外的例子:

比如,我们下班回家有多种途径,1、坐公交 2、骑自行车 3、网约车  4、出租车  5、摩的  6、开车 7、走路  8、跑步 9、骑马  10、地铁 ,如果我们把回家看作一个对象的话,这个对象的行为方式实在多变。倒不是说,回家这个对象构造有多么复杂,而是我们能不能根据交通情况,自由地选择回家模式。

比如,在天空中可以飞的东西:1、飞机  2、鸟 3、风筝 4、气球  5、子弹  6、弓箭  7、孔明灯  8、 云朵 9、 雪花 10、ufo,如果我们把这些东西制造出来,是不是可以飞呢?倒不是说飞有多么难,关键是制造这些东西比较难,而且工艺都不同。

再回到我们玩彩票上来,倒不是玩彩票有多难,而是玩彩票的玩法实在太多,不同的玩法,意味着,中奖率的计算方法是不同的。所以这里着重是行为方式,用策略模式是恰当的选择。

    

    

 

posted @ 2016-09-30 14:13 micDavid 阅读(...) 评论(...) 编辑 收藏