9.3.6 策略模式
9.3.6 策略模式
策略模式用于封装系列的算法,这些算法通常被封装在一个被称为Context
的类中,客户端程序可以自由选择其中一种算法,或让Context
为客户端选择一个最佳的算法—使用策略模式的优势是为了支持算法的自由切换。
考虑如下场景:假如正在开发一个网上书店,该书店为了更好地促销,经常需要对图书进行打折促销,程序需要考虑各种打折促销的计算方法。
为了实现书店现在所提供的各种打折需求,程序考虑使用如下方式来实现。
1 | //一段实现`discount()`方法的代码 |
上面的粗体字代码会根据打折类型来决定使用不同的打折算法,从而满足该书店促销打折的要求。从功能实现的角度来看,这段代码没有太大的问题。但这段代码有一个明显的不足,程序中各种打折方法都被直接写入了discount(double price)
方法中。
如有一天,该书店需要新增一种打折类型呢?那开发人员必须修改至少三处代码:
- 首先需要增加一个常量,该常量代表新增的打折类型;
- 其次需要在
switch
语句中增加一个case
语句; - 最后开发人员需要实现
xxxDiscount()
方法,用于实现新增的打折算法
为了改变这种不好的设计,下面将会选择使用策略模式来实现该功能。
程序示例
1 | E:\workspace_QingLiangJiJavaEEQiYeYingYongShiZhang5\Strategy |
下面先提供一个打折算法的接口,该接口里包含一个getDiscount()
方法,该接口的代码如下。
1 | public interface DiscountStrategy |
下面为该打折接口提供两个策略类,它们分别实现了不同的打折算法。
1 | // 实现DiscountStrategy接口,实现对VIP打折的算法 |
1 | public class OldDiscount implements DiscountStrategy |
提供了如上两个折扣策略类之后,程序还应该提供一个DiscountContext
类,该类用于为客户端代码选择合适折扣策略,当然也允许用户自由选择折扣策略。下面是该DiscountContext
类的代码。
1 | public class DiscountContext |
从上面的程序的粗体字代码可以看出,该Context
类扮演了决策者的角色,它决定调用哪个折扣策略来处理图书打折。当客户端代码没有选择合适的折扣时,该Context
会自动选择OldDiscount
折扣策略:用户也可根据需要选择合适的折扣策略。
下面的程序示范了使用该Context
类来处理图书打折的任何情况。
1 | public class StrategyTest |
上面程序的第一行粗体字代码创建了一个Discountcontext
对象,客户端并未指定实际所需的打折策略类,故程序将使用默认的打折策略类;程序第二行粗体字代码指定使用VipDiscount
策略类,故程序将改为使用VP打折策略。
从上面的介绍中可以看岀,使用策略模式可以让客户端代码在不同的打折策略之间切换,但也有个小小的遗憾:客户端代码需要和不同的策略类耦合。为了弥补这个不足,可以考虑使用配置文件来指定DiscountContext
使用哪种打折策略——这就彻底分离客户端代码和具体打折策略类
介绍到这里,相信读者对Hibernate
的Dialect
会有一点感觉了,这个Dialect
类代表各数据库方言的抽象父类,但不同数据库的持久化访问可能存在一些差别,尤其在分页算法上存在较大的差异, Dialect
不同子类就代表了一种特定的数据库访问策略。为了让客户端代码与具体的数据库、具体的Dialect
实现类分离, Hibernate
需要在hibernate.cfg.xml
文件中指定应用所使用的Dialect
子类。
与此类似的是, Spring
的Resource
接口也是一个典型的策略接口,不同的实现类代表了不同的资源访问策略。当然Spring
可以非常”智能”地选择合适的Resource
实现类,通常来说, Spring
可以根据前缀来决定使用合适的Resource
实现类;还可根据Application Context
的实现类来决定使用合适的Resource
实现类。具体请参考本书8.3节的介绍。