买衣服计价问题——策略模式与OCP原则
假设某服装店正在进行促销活动。
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;
-
-
public double getPrice() {
-
return policy.getPrice(price);
-
}
-
public void setPrice(double price) {
-
this.price = price;
-
}
-
return policy;
-
}
-
this.policy = policy;
-
}
-
-
}
[Policy.java] 计价类
-
-
public double getPrice(double price){
-
return price;
-
}
-
}
[DiscountPolicy.java] 折扣计价类
-
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] 促销计价类
-
-
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、 因为每个具体策略类都会产生一个新类,所以会增加系统需要维护的类的数量。 |