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

假设某服装店正在进行促销活动。
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、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值