Liny_@NotePad

沉迷ACG中

买衣服计价问题——策略模式与OCP原则

YOYO posted @ 2009年4月07日 17:12 in 【OOA/D】 with tags Strategy 模式 开闭原则 OCP , 3458 阅读

假设某服装店正在进行促销活动。
T恤打八折,毛衣打五折,而衬衫价格不变,
怎样获得要购买的衣服的实际出售价格?
第一种实现方式:
[Clothes.java] 衣服类

  1. public abstract class Clothes {
  2. private double price;
  3. public double getPrice() {
  4.   return price;
  5. }
  6. public void setPrice(double price) {
  7.   this.price = price;
  8. }
  9. }

[Shirt.java] 衬衫类

  1. public class Shirt extends Clothes {
  2. }

 

[Sweater.java] 毛衣类

  1. public class Sweater extends Clothes {
  2. }

 

[TShirt.java] T恤类

  1. public class TShirt extends Clothes {
  2. }

 

[CountClothesPriceSum.java] 计算总价类

  1. import java.util.*;
  2.  
  3. public class CountClothesPriceSum {
  4.  
  5. private List<Clothes> list = new ArrayList<Clothes>();
  6.  
  7. public void addToList(Clothes clothes){
  8.   list.add(clothes);
  9. }
  10.  
  11. public double countPriceSum(){
  12.   double sum = 0.0;
  13.   for(int i=0; i<list.size(); i++){
  14.    if(list.get(i) instanceof TShirt){
  15.       sum += list.get(i).getPrice()*0.8;
  16.    }else{
  17.       if(list.get(i) instanceof Sweater){
  18.          sum += list.get(i).getPrice()*0.5;
  19.       }else{
  20.          sum += list.get(i).getPrice();
  21.       }
  22.    }
  23.   }
  24.   return sum;
  25. }
  26. }

 

若此时,裙子跳楼价二折,又要如何?
我们怎样使所编写的代码能够扩展?

看看下面的实现:
//忘记怎么写了 - -。。 且先放着。。
// 总之仿佛是做一个Discount的接口,
// 让Clothes去实现它

// 它有一个discount属性,
// 即使不打折扣,也可以设置其为1

也许这是一个能解决打折问题的好方法,
但是……

但是,如果再来一个促销……满100返还20呢?

我们再定义一个Onsale的接口,让Clothes去实现?
不不不,这样做的话,
促销的产品多余了打折扣的方法,
而打折扣的产品则会多余促销的方法……
那么,我们该如何实现呢?

因此,我们在这里使用策略模式:

[Clothes.java]

  1. public abstract class Clothes {
  2. private double price;
  3.  
  4. private Policy policy = new Policy();
  5. public double getPrice() {
  6.   return policy.getPrice(price);
  7. }
  8. public void setPrice(double price) {
  9.   this.price = price;
  10. }
  11. public Policy getPolicy() {
  12.   return policy;
  13. }
  14. public void setPolicy(Policy policy) {
  15.   this.policy = policy;
  16. }
  17.  
  18. }

 

[Policy.java] 计价类

  1. public class Policy {
  2.  
  3. public double getPrice(double price){
  4.   return price;
  5. }
  6. }

 

[DiscountPolicy.java] 折扣计价类

  1. public class DiscountPolicy extends Policy {
  2. private double discount;
  3.  
  4. public DiscountPolicy(double discount){
  5.   this.discount = discount;
  6. }
  7. public void setDiscount(double discount) {
  8.   this.discount = discount;
  9. }
  10.  
  11. public double getPrice(double price){
  12.   return price*discount;
  13. }
  14. }

 

[OnsalePolicy.java] 促销计价类

  1. public class OnsalePolicy extends Policy {
  2.  
  3. private int basePrice;
  4.  
  5. private double returnMoney;
  6.  
  7. public OnsalePolicy(int basePrice, double returnMoney){
  8.   this.basePrice = basePrice;
  9.   this.returnMoney = returnMoney;
  10. }
  11.  
  12. public double getPrice(double price){
  13.   if(price>=basePrice){
  14.    return price-returnMoney;
  15.   }
  16.   return price;
  17. }
  18. public void setBasePrice(int basePrice){
  19.   this.setBasePrice(basePrice);
  20. }
  21.  
  22. public void setReturnMoney(int returnMoney){
  23.   this.returnMoney = returnMoney;
  24. }
  25.  
  26. }

 

[TShirt.java][Shirt.java][Sweater.java][Skirt.java]等衣服的子类略。
[CountClothesPriceSum.java] 计算总价类

  1. public class CountClothesPriceSum {
  2.  
  3. private List<Clothes> list = new ArrayList<Clothes>();
  4.  
  5. public void addToList(Clothes clothes){
  6.   list.add(clothes);
  7. }
  8.  
  9. public double countPriceSum(){
  10.   double sum = 0.0;
  11.   for(int i=0; i<list.size(); i++){
  12.    sum += list.get(i).getPrice();
  13.   }
  14.   return sum;
  15. }
  16. }

 

这样,如果还有新的计价策略,
我们只需要写一个类来继承Policy,重写它的getPrice(double)方法即可。
这便做到了“对扩展开放,对修改关闭。”

最后附上改进用的UT:

  1. public class TestCases extends TestCase {
  2. public void testCase1(){
  3.   CountClothesPriceSum count = new CountClothesPriceSum();
  4.  
  5.   Shirt shirt = new Shirt();
  6.   shirt.setPrice(32);
  7.   count.addToList(shirt);
  8.   assertEquals(count.countPriceSum(), 32.0);
  9.  
  10.   TShirt tShirt = new TShirt();
  11.   tShirt.setPrice(20);
  12.   tShirt.setPolicy(new DiscountPolicy(0.8));
  13.   count.addToList(tShirt);
  14.   assertEquals(count.countPriceSum(), 48.0);
  15.  
  16.   DiscountPolicy discountPolicy = new DiscountPolicy(0.5);
  17.   tShirt.setPolicy(discountPolicy);
  18.   assertEquals(count.countPriceSum(), 42.0);
  19.  
  20.   Sweater sweater = new Sweater();
  21.   sweater.setPrice(50);
  22.   sweater.setPolicy(discountPolicy);
  23.   count.addToList(sweater);
  24.   assertEquals(count.countPriceSum(), 67.0);
  25. }
  26. // For OnsalePolicy
  27. public void testCase2(){
  28.   CountClothesPriceSum count = new CountClothesPriceSum();
  29.  
  30.   Shirt shirt = new Shirt();
  31.   shirt.setPolicy(new OnsalePolicy(100,20));
  32.   shirt.setPrice(80);
  33.   count.addToList(shirt);
  34.   assertEquals(count.countPriceSum(), 80.0);
  35.  
  36.   shirt.setPrice(100);
  37.   assertEquals(count.countPriceSum(), 80.0);
  38.  
  39.   shirt.setPrice(120);
  40.   assertEquals(count.countPriceSum(), 100.0);
  41.  
  42.   OnsalePolicy onsalePolicy = new OnsalePolicy(50,5);
  43.   Sweater sweater = new Sweater();
  44.   sweater.setPrice(75);
  45.   sweater.setPolicy(onsalePolicy);
  46.   count.addToList(sweater);
  47.   assertEquals(count.countPriceSum(), 170.0);
  48.  
  49.   sweater.setPrice(20);
  50.   assertEquals(count.countPriceSum(), 120.0);
  51. }
  52.  
  53. // For Skirt
  54. public void testCase3(){
  55.   CountClothesPriceSum count = new CountClothesPriceSum();
  56.   Skirt skirt = new Skirt();
  57.   skirt.setPrice(50);
  58.   count.addToList(skirt);
  59.   assertEquals(count.countPriceSum(), 50.0);
  60.  
  61.   skirt.setPolicy(new DiscountPolicy(0.3));
  62.   assertEquals(count.countPriceSum(), 15.0);
  63.  
  64.   skirt.setPolicy(new OnsalePolicy(30,5));
  65.   assertEquals(count.countPriceSum(), 45.0);
  66.  
  67. }
  68. }

策略模式(Strategy)

 

 

  策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。(The Strategy Pattern defines a family of algorithms,encapsulates each one,and makes them interchangeable. Strategy lets the algorithm vary independently from clients that use it.)
应用场景
1、 多个类只区别在表现行为不同,可以使用Strategy模式,在运行时动态选择具体要执行的行为。(例如FlyBehavior和QuackBehavior)
2、 需要在不同情况下使用不同的策略(算法),或者策略还可能在未来用其它方式来实现。(例如FlyBehavior和QuackBehavior的具体实现可任意变化或扩充)
3、 对客户(Duck)隐藏具体策略(算法)的实现细节,彼此完全独立。

优点
1、 提供了一种替代继承的方法,而且既保持了继承的优点(代码重用)还比继承更灵活(算法独立,可以任意扩展)。
2、 避免程序中使用多重条件转移语句,使系统更灵活,并易于扩展。
3、 遵守大部分GRASP原则和常用设计原则,高内聚、低偶合。

缺点
1、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter