您的位置:首页 > 其它

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

2009-08-23 16:11 246 查看
假设某服装店正在进行促销活动。

T恤打八折,毛衣打五折,而衬衫价格不变,

怎样获得要购买的衣服的实际出售价格?

第一种实现方式:

[Clothes.java] 衣服类


public abstract class Clothes {



private double price;



public double getPrice() {



return price;



}



public void setPrice(double price) {



this.price = price;



}



}


[Shirt.java] 衬衫类


public class Shirt extends Clothes {



}




[Sweater.java] 毛衣类


public class Sweater extends Clothes {



}




[TShirt.java] T恤类


public class TShirt extends Clothes {



}




[CountClothesPriceSum.java] 计算总价类


import java.util.*;







public class CountClothesPriceSum {







private List<Clothes> list = new ArrayList<Clothes>();







public void addToList(Clothes clothes){



list.add(clothes);



}







public double countPriceSum(){



double sum = 0.0;



for(int i=0; i<list.size(); i++){



if(list.get(i) instanceof TShirt){



sum += list.get(i).getPrice()*0.8;



}else{



if(list.get(i) instanceof Sweater){



sum += list.get(i).getPrice()*0.5;



}else{



sum += list.get(i).getPrice();



}



}



}



return sum;



}



}




若此时,裙子跳楼价二折,又要如何?

我们怎样使所编写的代码能够扩展?

看看下面的实现:

//忘记怎么写了 - -。。 且先放着。。

// 总之仿佛是做一个Discount的接口,

// 让Clothes去实现它

// 它有一个discount属性,

// 即使不打折扣,也可以设置其为1

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

但是……

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

我们再定义一个Onsale的接口,让Clothes去实现?

不不不,这样做的话,

促销的产品多余了打折扣的方法,

而打折扣的产品则会多余促销的方法……

那么,我们该如何实现呢?

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




[Clothes.java]


public abstract class Clothes {



private double price;







private Policy policy = new Policy();



public double getPrice() {



return policy.getPrice(price);



}



public void setPrice(double price) {



this.price = price;



}



public Policy getPolicy() {



return policy;



}



public void setPolicy(Policy policy) {



this.policy = policy;



}







}




[Policy.java] 计价类


public class Policy {







public double getPrice(double price){



return price;



}



}




[DiscountPolicy.java] 折扣计价类


public class DiscountPolicy extends Policy {



private double discount;







public DiscountPolicy(double discount){



this.discount = discount;



}



public void setDiscount(double discount) {



this.discount = discount;



}







public double getPrice(double price){



return price*discount;



}



}




[OnsalePolicy.java] 促销计价类


public class OnsalePolicy extends Policy {







private int basePrice;







private double returnMoney;







public OnsalePolicy(int basePrice, double returnMoney){



this.basePrice = basePrice;



this.returnMoney = returnMoney;



}







public double getPrice(double price){



if(price>=basePrice){



return price-returnMoney;



}



return price;



}



public void setBasePrice(int basePrice){



this.setBasePrice(basePrice);



}







public void setReturnMoney(int returnMoney){



this.returnMoney = returnMoney;



}







}




[TShirt.java][Shirt.java][Sweater.java][Skirt.java]等衣服的子类略。

[CountClothesPriceSum.java] 计算总价类


public class CountClothesPriceSum {







private List<Clothes> list = new ArrayList<Clothes>();







public void addToList(Clothes clothes){



list.add(clothes);



}







public double countPriceSum(){



double sum = 0.0;



for(int i=0; i<list.size(); i++){



sum += list.get(i).getPrice();



}



return sum;



}



}




这样,如果还有新的计价策略,

我们只需要写一个类来继承Policy,重写它的getPrice(double)方法即可。

这便做到了“对扩展开放,对修改关闭。”

最后附上改进用的UT:


public class TestCases extends TestCase {



public void testCase1(){



CountClothesPriceSum count = new CountClothesPriceSum();







Shirt shirt = new Shirt();



shirt.setPrice(32);



count.addToList(shirt);



assertEquals(count.countPriceSum(), 32.0);







TShirt tShirt = new TShirt();



tShirt.setPrice(20);



tShirt.setPolicy(new DiscountPolicy(0.8));



count.addToList(tShirt);



assertEquals(count.countPriceSum(), 48.0);







DiscountPolicy discountPolicy = new DiscountPolicy(0.5);



tShirt.setPolicy(discountPolicy);



assertEquals(count.countPriceSum(), 42.0);







Sweater sweater = new Sweater();



sweater.setPrice(50);



sweater.setPolicy(discountPolicy);



count.addToList(sweater);



assertEquals(count.countPriceSum(), 67.0);



}



// For OnsalePolicy



public void testCase2(){



CountClothesPriceSum count = new CountClothesPriceSum();







Shirt shirt = new Shirt();



shirt.setPolicy(new OnsalePolicy(100,20));



shirt.setPrice(80);



count.addToList(shirt);



assertEquals(count.countPriceSum(), 80.0);







shirt.setPrice(100);



assertEquals(count.countPriceSum(), 80.0);







shirt.setPrice(120);



assertEquals(count.countPriceSum(), 100.0);







OnsalePolicy onsalePolicy = new OnsalePolicy(50,5);



Sweater sweater = new Sweater();



sweater.setPrice(75);



sweater.setPolicy(onsalePolicy);



count.addToList(sweater);



assertEquals(count.countPriceSum(), 170.0);







sweater.setPrice(20);



assertEquals(count.countPriceSum(), 120.0);



}







// For Skirt



public void testCase3(){



CountClothesPriceSum count = new CountClothesPriceSum();



Skirt skirt = new Skirt();



skirt.setPrice(50);



count.addToList(skirt);



assertEquals(count.countPriceSum(), 50.0);







skirt.setPolicy(new DiscountPolicy(0.3));



assertEquals(count.countPriceSum(), 15.0);







skirt.setPolicy(new OnsalePolicy(30,5));



assertEquals(count.countPriceSum(), 45.0);







}



}


策略模式(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、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: