设计模式之策略模式

做一个商场收银软件。根据商场的特点,有时候打9折,有时候打3折;有时候满300减100,有时候满500减100;有时候消费100积10分,够100积分换什么等等,有着很多复杂的销售方式,而且特点是可能经常性的更改打折额度和返利额度。

首先,用简单工厂来实现下:

 1 //现金收费抽象类
 2     abstract class SaleOperation
 3     {
 4         //参数为原价,返回值为实际成交价格
 5         public abstract double LastPrice(double money);
 6     }
 7 
 8     //正常收费
 9     class SaleOperationNormal : SaleOperation
10     {
11         public override double LastPrice(double money)
12         {
13             return money;
14         }
15     }
16 
17     //打折
18     class SaleOperationByCutOff:SaleOperation
19     {
20         //注意:1d表示双精度浮点数
21         private double _cutOffRadio = 1d;
22         //折扣,需要传入具体是3折还是9折
23         public SaleOperationByCutOff(double cutOffRadio)
24         {
25             this._cutOffRadio = cutOffRadio;
26         }
27         public override double LastPrice(double money)
28         {
29             return money * _cutOffRadio;
30         }
31     }
32     //返利收费
33     class SaleOperationByEnoughSend : SaleOperation
34     {
35         private double _moneyCondition = 0.0d;
36         private double _moneyReturn = 0.0d;
37         //传入返利条件和返利值,如满300减100,则moneyCondition=300,moneyReturn=100
38         public SaleOperationByEnoughSend(double moneyCondition, double moneyReturn)
39         {
40             this._moneyCondition = moneyCondition;
41             this._moneyReturn = moneyReturn;
42         }
43         public override double LastPrice(double money)
44         {
45             double result = money;
46             //符合返利条件,则实际收取的钱要减去返利值
47             if (money > _moneyCondition)
48                 result = money - Math.Floor(money / _moneyCondition) * _moneyReturn;
49             return result;
50         }
51     }
框架设计
 1 class SaleOperationFactroy
 2     {
 3         //type 为促销方式选择,1为打折,2为返利等等
 4         public static SaleOperation createSaleOperation(int type)
 5         {
 6             SaleOperation so = null;
 7             switch (type)
 8             {
 9                 case 1:
10                     so = new SaleOperationByCutOff(0.8);//必须在这里确定是八折,如果换成了7折,需要修改代码
11                     break;
12                 case 2:
13                     so = new SaleOperationByEnoughSend(300,100);//必须在这里确定是300返100,如果是500返100,需要修改代码
14                     break;
15             }
16             return so;
17         }
18     }
工厂类
 1  double totalPrice = 0d;
 2         public void btnOK()
 3         {
 4             double thisPrice = 0d;
 5             SaleOperation so = SaleOperationFactroy.createSaleOperation(1);
 6             //根据多态,取得本次商品实际价格(单品价格*数量)
 7             thisPrice = so.LastPrice(Convert.ToDouble(txtPrice.Text)) * Convert.ToDouble(txtCount.Text);
 8             //计入总价
 9             totalPrice = totalPrice + thisPrice;
10         }
客户端

由此可见简单工厂的弊端:商场经常性的变更折扣额度和返利额度的时候,每次都要对工厂类进行修改,重新编译源码,重新编译部署,不能应对算法的时常变动。

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

策略模式是定义了一系列算法的方法,从概念上来看,所有的算法完成的都是相同的工作,只是实现不同,它可以以相同的方式调用所有算法,减少了各种算法类与使用算法类之间的耦合。另外采取继承有助于析取出这些算法的公共功能。

商场收银时如何促销,用打折还是返利,其实都是一些算法,用工厂生成算法本身没错,但是算法本身只是一种策略,最重要的是这些算法是随时都可能互相替换的,这是关键变化点,而封装变化点是面向对象一种很重要的思维方式。

策略模式:

 1 //现金收费抽象类
 2     abstract class SaleOperation
 3     {
 4         //参数为原价,返回值为实际成交价格
 5         public abstract double LastPrice(double money);
 6     }
 7 
 8     //正常收费
 9     class SaleOperationNormal : SaleOperation
10     {
11         public override double LastPrice(double money)
12         {
13             return money;
14         }
15     }
16 
17     //打折
18     class SaleOperationByCutOff:SaleOperation
19     {
20         //注意:1d表示双精度浮点数
21         private double _cutOffRadio = 1d;
22         //折扣,需要传入具体是3折还是9折
23         public SaleOperationByCutOff(double cutOffRadio)
24         {
25             this._cutOffRadio = cutOffRadio;
26         }
27         public override double LastPrice(double money)
28         {
29             return money * _cutOffRadio;
30         }
31     }
32     //返利收费
33     class SaleOperationByEnoughSend : SaleOperation
34     {
35         private double _moneyCondition = 0.0d;
36         private double _moneyReturn = 0.0d;
37         //传入返利条件和返利值,如满300减100,则moneyCondition=300,moneyReturn=100
38         public SaleOperationByEnoughSend(double moneyCondition, double moneyReturn)
39         {
40             this._moneyCondition = moneyCondition;
41             this._moneyReturn = moneyReturn;
42         }
43         public override double LastPrice(double money)
44         {
45             double result = money;
46             //符合返利条件,则实际收取的钱要减去返利值
47             if (money > _moneyCondition)
48                 result = money - Math.Floor(money / _moneyCondition) * _moneyReturn;
49             return result;
50         }
51     }
52 
53     //****核心***** 定义一个上下文类
54     //Context类的功能基本上就是对工厂类的强化版本,它也负责根据输入参数来生成不同的类,只是它并不返回生成类,
55     //而是将生成类所实现的功能接口包装一次,提供给客户。
56     //这样对客户来说,他们只需要知道这一个Context类就可以完成他们想要的功能,而不必再知道其他的信息。
57     class SaleContext
58     {
59         //生命一个现金收费抽象类对象
60         SaleOperation _sc;
61         //通过构造方法,传入具体的收费策略
62         public SaleContext(SaleOperation sc)
63         {
64             this._sc = sc;
65         }
66         //根据传入的具体对象,得到不同的最终价格
67         public double GetResult(double money)
68         {
69             return _sc.LastPrice(money);
70         }
71     }
框架设计
 1 public void btnOK()
 2         {
 3             //定义一个上下文类
 4             SaleContext sc = null;
 5             switch (type)
 6             {
 7                 case 1:
 8                     sc = new SaleContext(new SaleOperationByCutOff(0.8));
 9                     break;
10                 case 2:
11                     sc = new SaleContext(new SaleOperationByEnoughSend(300,100));
12                     break;
13                     
14             }
15             double thisPrice = 0d;
16             SaleOperation so = SaleOperationFactroy.createSaleOperation(1);
17             //根据多态,对上下文类调用其方法。取得最终收取费用结果,让具体算法和客户进行了隔离
18             thisPrice = sc.GetResult(Convert.ToDouble(txtPrice.Text)) * Convert.ToDouble(txtCount.Text);
19             //计入总价
20             totalPrice = totalPrice + thisPrice;
21         }
客户端

策略模式和工厂模式类似,还是负责根据输入参数的不同来生成不同的子类,并将生成的子类作为基类返回(这样可以让客户端在使用的时候很方便),不过把选择的地方放在了客户端。客户端只需要调用上下文类创建一个基类的实例,然后调用这个实例的函数来实现自己的功能即可。

策略模式比起工厂模式来更强大,它可以在运行中对这个属性进行改变,而工厂模式中使用的是一个对象,对象就没法在运行中改变了,没办法在运行中随时变成其它分支类。这一点上来说,策略模式应该是比工厂模式更灵活的一个加强版。

策略模式使用时必须在客户端首先创建一个想使用的类对象,然后将该对象最为参数传递进去,通过该对象调用不同的算法,因为策略模式中并没有为我们实现具体实例的选取,只是从抽象类中调用了统一接口。在简单工厂模式中实现了通过条件选取一个类去实例化对象,策略模式则将选取相应对象的工作交给模式的使用者,它本身不去做选取工作。

策略模式就是用来封装算法的,在实践中,发现可以用它来封装几乎任何类型的规则,只要在分析过程中听到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。

和工厂模式的区别是,在工厂模式中,这个变量是个对象,而在策略模式中,这个变量成了对象的一个属性。另外,策略模式耦合度更低。

工厂模式客户端:需要认识两个类SaleOperationSaleOperationFactroy

SaleOperation so = SaleOperationFactroy.createSaleOperation(1);

thisPrice = so.LastPrice(Convert.ToDouble(txtPrice.Text)) * Convert.ToDouble(txtCount.Text);

策略模式+工厂模式客户端:只需要一个类SaleContext

SaleContext sc = new SaleContext(1);

thisPrice = sc.GetResult(Convert.ToDouble(txtPrice.Text)) * Convert.ToDouble(txtCount.Text);

策略模式具体适用于:

     1.以不同的格式保存文件;

  2.以不同的算法压缩文件;

  3.以不同的算法截获图象;

  4.以不同的格式输出同样数据的图形,比如曲线 或框图bar等。

策略模式+简单工厂:仍然没有解决SWITCH问题。如果需要解决,必须用到反射

 1         public void btnOK()
 2         {
 3             //定义出上下文类的对象。
 4             SaleContext sc = new SaleContext(1);
 5             double thisPrice = 0d;
 6             
 7             //根据多态,对上下文类调用其方法。取得最终收取费用结果,让具体算法和客户进行了隔离
 8             thisPrice = sc.GetResult(Convert.ToDouble(txtPrice.Text)) * Convert.ToDouble(txtCount.Text);
 9             //计入总价
10             totalPrice = totalPrice + thisPrice;
11         }
客户端
 1 //现金收费抽象类
 2     abstract class SaleOperation
 3     {
 4         //参数为原价,返回值为实际成交价格
 5         public abstract double LastPrice(double money);
 6     }
 7 
 8     //正常收费
 9     class SaleOperationNormal : SaleOperation
10     {
11         public override double LastPrice(double money)
12         {
13             return money;
14         }
15     }
16 
17     //打折
18     class SaleOperationByCutOff:SaleOperation
19     {
20         //注意:1d表示双精度浮点数
21         private double _cutOffRadio = 1d;
22         //折扣,需要传入具体是3折还是9折
23         public SaleOperationByCutOff(double cutOffRadio)
24         {
25             this._cutOffRadio = cutOffRadio;
26         }
27         public override double LastPrice(double money)
28         {
29             return money * _cutOffRadio;
30         }
31     }
32     //返利收费
33     class SaleOperationByEnoughSend : SaleOperation
34     {
35         private double _moneyCondition = 0.0d;
36         private double _moneyReturn = 0.0d;
37         //传入返利条件和返利值,如满300减100,则moneyCondition=300,moneyReturn=100
38         public SaleOperationByEnoughSend(double moneyCondition, double moneyReturn)
39         {
40             this._moneyCondition = moneyCondition;
41             this._moneyReturn = moneyReturn;
42         }
43         public override double LastPrice(double money)
44         {
45             double result = money;
46             //符合返利条件,则实际收取的钱要减去返利值
47             if (money > _moneyCondition)
48                 result = money - Math.Floor(money / _moneyCondition) * _moneyReturn;
49             return result;
50         }
51     }
52 
53     //****核心***** 定义一个上下文类
54     //Context类的功能基本上就是对工厂类的强化版本,它也负责根据输入参数来生成不同的类,只是它并不返回生成类,
55     //而是将生成类所实现的功能接口包装一次,提供给客户。
56     //这样对客户来说,他们只需要知道这一个Context类就可以完成他们想要的功能,而不必再知道其他的信息。
57     class SaleContext
58     {
59         //声明一个现金收费抽象类对象
60         SaleOperation _sc;
61 
62         public SaleContext(int type)
63         {
64             switch (type)
65             {
66                 case 1:
67                     SaleOperationByCutOff sbc = new SaleOperationByCutOff(0.8);  //根据TYPE定义出返利具体信息,然后利用多态将对象定义回父类
68                     _sc = sbc;
69                     break;
70                 case 2:
71                     SaleOperationByEnoughSend sbes = new SaleOperationByEnoughSend(300,100);
72                     _sc = sbes;
73                     break;
74             }
75         }
76         //根据传入的具体对象,得到不同的最终价格
77         public double GetResult(double money)
78         {
79             return _sc.LastPrice(money);
80         }
81     }
框架设计

posted on 2013-08-01 11:31  冲刺  阅读(171)  评论(0)    收藏  举报

导航