封装打折策略

.3.6 策略模式

策略模式用于封装系列的算法,这些算法通常被封装在一个被称为Context的类中,客户端程序可以自由选择其中一种算法,或让Context为客户端选择一个最佳的算法——使用策略模式的优势是为了支持算法的自由切换。

考虑如下场景:现在我们正在开发一个网上书店,该书店为了更好地促销,经常需要对图书进行打折促销,程序需要考虑各种打折促销的计算方法。

为了实现书店现在所提供的各种打折需求,程序考虑使用如下方式来实现。

 

//一段实现discount()方法代码
public double discount(double price) 
{
//针对不同情况采用不同的打折算法
switch(getDiscountType()) 
{
case VIP_DISCOUNT: 
return vipDiscount(price);
case OLD_DISCOUNT:
return oldDiscount(price);
case SALE_DISCOUNT:
return saleDiscount(price);
...
}
}

 

上面粗体字代码会根据打折类型来决定使用不同的打折算法,从而满足该书店促销打折的要求。从功能实现的角度来看,这段代码没有太大的问题。但这段代码有一个明显的不足,程序中各种打折方法都被直接写入了discount(double price)方法中。如有一天,该书店需要新增一种打折类型呢?那开发人员必须修改至少3处代码:首先需要增加一个常量,该常量代表新增的打折类型;其次需要在switch语句中增加一个case语句;最后开发人员需要实现xxxDiscount()方法,用于实现新增的打折算法。

为了改变这种不好的设计,下面将会选择使用策略模式来实现该功能。下面先提供一个打折算法的接口,该接口里包含一个getDiscount ()方法,该接口代码如下:

程序清单:codes\09\9.3\Strategy\DiscountStrategy.java

 

public interface DiscountStrategy
{
//定义一个用于计算打折价的方法
double getDiscount(double originPrice); 
}

 

下面为该打折接口提供两个策略类,它们分别实现了不同的打折算法。

程序清单:codes\09\9.3\Strategy\VipDiscount.java

 

//实现DiscountStrategy接口,实现对VIP打折的算法
public class VipDiscount 
implements DiscountStrategy
{
//重写getDiscount()方法,提供VIP打折算法
public double getDiscount(double originPrice)
{
System.out.println("使用VIP折扣...");
return originPrice * 0.5;
}
}
程序清单:codes\09\9.3\Strategy\OldDiscount.java
public class OldDiscount
implements DiscountStrategy
{
//重写getDiscount()方法,提供旧书打折算法
public double getDiscount(double originPrice)
{
System.out.println("使用旧书折扣...");
return originPrice * 0.7;
}
}

 

提供了如上两个折扣策略类之后,程序还应该提供一个DiscountContext类,该类用于为客户端代码选择合适折扣策略,当然也允许用户自由选择折扣策略。下面是该DiscountContext类的代码。

程序清单:codes\09\9.3\Strategy\DiscountContext.java

 

public class DiscountContext
{
//组合一个DiscountStrategy对象
private DiscountStrategy strategy;
//构造器,传入一个DiscountStrategy对象
public DiscountContext(DiscountStrategy strategy)
{
this.strategy  = strategy;
}
//根据实际所使用的DiscountStrategy对象得到折扣价
public double getDiscountPrice(double price) 
{
//如果strategy为null,系统自动选择OldDiscount类
if (strategy == null)
{
strategy = new OldDiscount();
}
return this.strategy.getDiscount(price);
}
//提供切换算法的方法
public void changeDiscount(DiscountStrategy strategy)
{
this.strategy = strategy;
}
}

 

从上面程序的粗体字代码可以看出,该Context类扮演了决策者的角色,它决定调用哪个折扣策略来处理图书打折。当客户端代码没有选择合适的折扣时,该Context会自动选择OldDiscount折扣策略;用户也可根据需要选择合适的折扣策略。

下面程序示范了使用该Contex类来处理图书打折的任何情况。

程序清单:codes\09\9.3\Strategy\StrategyTest.java

 

public class StrategyTest
{
public static void main(String[] args) 
{
//客户端没有选择打折策略类
DiscountContext dc = new DiscountContext(null);
double price1 = 79;
//使用默认的打折策略
System.out.println("79元的书默认打折后的价格是:" 
+ dc.getDiscountPrice(price1));
//客户端选择合适的VIP打折策略
dc.setDiscount(new VipDiscount());
double price2 = 89;
//使用VIP打折得到打折价格
System.out.println("89元的书对VIP用户的价格是:" 
+ dc.getDiscountPrice(price2));
}
}

 

上面程序第一行粗体字代码创建了一个DiscountContext对象,客户端并未指定实际所需的打折策略类,故程序将使用默认的打折策略类;程序第二行粗体字代码指定使用VipDiscount策略类,故程序将改为使用VIP打折策略。

再次考虑前面的需求:当业务需要新增一种打折类型时,系统只需要新定义一个DiscountStrategy实现类,该实现类实现getDiscount()方法,用于实现新的打折算法即可。客户端程序需要切换为新的打折策略时,则需要先调用DiscountContext的setDiscount()方法切换为新的打折策略。

从上面介绍中可以看出,使用策略模式可以让客户端代码在不同的打折策略之间切换,但也有一个小小的遗憾:客户端代码需要和不同的策略类耦合。为了弥补这个不足,我们可以考虑使用配置文件来指定DiscountContext使用哪种打折策略——这就彻底分离客户端代码和具体打折策略类。

介绍到这里,相应读者对Hibernate的Dialect会有一点感觉了,这个Dialect类代表各数据库方言的抽象父类,但不同数据库的持久化访问可能存在一些差别,尤其在分页算法上存在较大的差异,Dialect不同子类就代表了一种特定的数据库访问策略。为了让客户端代码与具体的数据库、具体的Dialect实现类分离,Hibernate需要在hibernate.cfg.xml文件中指定应用所使用的Dialect子类。

与此类似的是,Spring的Resource接口也是一个典型的策略接口,不同的实现类代表了不同的资源访问策略。当然Spring可以非常“智能”地选择合适的Resource实现类,通常来说,Spring可以根据前缀来决定使用合适的Resource实现类;还可根据ApplicationContext的实现类来决定使用合适的Resource实现类。具体请参考本书8.3节介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值