策略模式/Strategy
意图/适用场景:
策略模式是针对一组算法,将每一个算法封装到具有共同接口的独立类中,从而使得它们可以互换。这样做的好处是,客户端可以以插件的方式更换算法。
电子商务网站的购物车系统是一个策略模式非常适用的场景:比如,在优惠方式上,某一类商品是每件优惠一元,另一类商品是折扣5%,还有一类商品也是打折,但折扣幅度是10%。所以需要应用不同的优惠算法来计算最终的价格。
在这个系统中,可以想象到会有一个计算者角色(与本模式中的Context相同功能),它负责计算价格。但如果把所有的算法都放在它里面来实现的话,会比较混乱。如果应用策略模式的话,就可以把行为和上下文环境分开。上下文类(计算者)中负责维持和查询行为类,各种算法则在具体策略类中提供。由于算法和环境独立开来,算法的增减、修改都不会影响到上下文和客户端。这也为上下文类减轻了负担。
UML:
参与者:
- 上下文角色(Context):持有一个Strategy类的引用。
- 抽象策略角色(Strategy):抽象角色,通常是一个接口或者抽象类。定义出所有的具体策略类的公共接口。如果具体策略类有共同的行为,可以把Strategy定义为抽象类,并实现这些公共的行为;否则最好定义为接口。
- 具体策略(ConcreteStrategy):包装了相关的算法或行为。
要点:
在下面的情况下应当考虑使用策略模式:
- 系统需要动态地在多种算法中选择一种;
- 算法很多,如果由一个角色来处理的话,逻辑会非常混乱;
- 算法不可让客户端知道。
策略模式的最大优点在于把算法分门别类,由不同的实体类来维护,实现了算法本身与算法的使用者之间的弱耦合。而且把一个搅在一起的大算法分而治之,避免了大量使用条件而造成的混乱。
应用实例:
排序策略系统,参见《Java与模式》本章。
相关模式:
以下模式在结构上与策略模式相似,它们的区别多半在于用意不同,适用的场景是不同的。
- 策略模式:强调的是算法的分治与选择。
- 建造模式:它的核心功能是以一步一步的方式创建一个产品,把各个零件分别制造并组装起来,最后形成一个产品。
- 适配模式:它的用意在于把不同接口的对象“转接”成客户端所需要的接口。
- 装饰模式:它的用意在于在不改变接口的情况下,增强一个对象的功能。
示例代码:
[java]
// Source code from file:ConcreteStrategyA.java
packagedesignPatterns.Strategy;
publicclass ConcreteStrategyA implements Strategy {
publicvoid strategy() {
System.out.println("ConcreteStrategyA.strategy()");
}
}
// Source code from file:ConcreteStrategyB.java
packagedesignPatterns.Strategy;
publicclass ConcreteStrategyB implements Strategy {
publicvoid strategy() {
System.out.println("ConcreteStrategyA.strategy()");
}
}
// Source code from file:Context.java
packagedesignPatterns.Strategy;
publicclass Context {
privateStrategy strategy;
publicvoid strategy(char type) {
if('A' == type)
strategy =new ConcreteStrategyA();
elseif ('B' == type)
strategy =new ConcreteStrategyB();
strategy.strategy();
}
}
// Source code from file:Strategy.java
packagedesignPatterns.Strategy;
publicinterface Strategy {
publicvoid strategy();
}
// Source code from file:User.java
packagedesignPatterns.Strategy;
publicclass User {
publicstatic void main(String[] args) {
Contextctxt = new Context();
ctxt.strategy('A');
ctxt.strategy('B');
}
}
[/java]