策略模式(Strategy)
策略模式简介
1.策略模式的定义: 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
2.策略模式举例:
先举个简单的例子:商场为了吸引顾客,采用了会员打折的方式来促销。 金牌会员打8折、银牌会员打9折、普通会员打9.5折,当顾客在商场购买了商品后根据会员的等级不同计算出最后的应付价格,面对这种情况在不使用策略模式的情况下我们可以在计算支付的程序里面采用 if/else 的形式来分别计算不同等级的顾客的应付价格。 但是,如果哪天商场加大了促销力度所有会员在原打折的基础上再打8折 该怎么办?或者商场推出会员生日当天消费满500返现50 该怎么办?
面对上面商场的打折情况,如果我们把所有的打折可能性都写到 if/else 里面,那只要有新的打折需求出现我们就会去改动这块的代码,我们都知道只要修改代码就有可能会引入新的问题,为了避免修改代码带来新的问题 我们可以使用策略模式来解决。
策略模式的结构和代码示例
1.策略模式的结构图
Strategy: 抽象策略类,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略实现定义的算法。
ConcreteStrategy: 具体的策略实现,也就是具体的算法实现。
Context: 上下文,负责和具体的策略类交互。通常上下文会持有一个真正的策略实现。
2.使用策略模式编写上面商场打折的案列
先定义一个接口,这个接口就是抽象策略类,该接口定义了计算商品价格的方法,具体的实现由具体的策略类来完成.
1 package com.strategy; 2 /** 3 * 抽象策略接口 4 * @author Administrator 5 * 6 */ 7 public interface PriceStrategy { 8 9 /** 10 * 计算商品应付价格 11 * @param goodsPrice 商品价格(针对不同等级的会员不同的折扣价 12 * @return 13 */ 14 public double calcPrice(double goodsPrice); 15 }
针对不同的会员等级,定义三种具体的策略类来计算打折后的商品价格,以下几个类体现了封装变化的设计原则,不同的会员等级有不同的折扣方案,它们之间相互没有影响。
金牌会员打8折:
1 package com.strategy; 2 3 /** 4 * 具体的策略类:金牌会员打8折 5 * @author Administrator 6 * 7 */ 8 public class GoldMember implements PriceStrategy { 9 10 @Override 11 public double calcPrice(double goodsPrice) { 12 double finalPrice = goodsPrice * 0.8; 13 return finalPrice; 14 } 15 }
银牌会员打9折:
1 package com.strategy; 2 3 /** 4 * 具体的策略类,银牌会员打9.5折 5 * @author Administrator 6 * 7 */ 8 public class SilverMember implements PriceStrategy { 9 10 @Override 11 public double calcPrice(double goodsPrice) { 12 double finalPrice = goodsPrice * 0.9; 13 return finalPrice; 14 } 15 }
普通会员打9.5折:
1 package com.strategy; 2 3 /** 4 * 具体的策略类:普通会员打9.5折 5 * @author Administrator 6 * 7 */ 8 public class NormalMember implements PriceStrategy { 9 10 @Override 11 public double calcPrice(double goodsPrice) { 12 double finalPrice = goodsPrice * 0.95; 13 return finalPrice; 14 } 15 }
上下文定义:
1 package com.strategy; 2 3 /** 4 * 上下文,负责和具体的策略类来进行交互 5 * @author Administrator 6 * 7 */ 8 public class PriceContext { 9 private PriceStrategy priceStrategy; 10 11 public PriceContext(PriceStrategy priceStrategy){ 12 this.priceStrategy = priceStrategy; 13 } 14 15 public void setPriceStrategy(PriceStrategy priceStrategy) { 16 this.priceStrategy = priceStrategy; 17 } 18 19 public double calcPrice(double goodsPrice){ 20 return priceStrategy.calcPrice(goodsPrice); 21 } 22 }
测试类定义:
1 package com.strategy; 2 3 public class Client { 4 public static void main(String[] args) { 5 //金牌会员计算商品价格 6 GoldMember goldMember = new GoldMember(); 7 PriceContext context = new PriceContext(goldMember); 8 System.out.println("金牌会员购买1000元商品,打折后应付: " + context.calcPrice(1000)); 9 10 //金牌会员计算商品价格 11 SilverMember silverMember = new SilverMember(); 12 context.setPriceStrategy(silverMember); 13 System.out.println("银牌会员购买1000元商品,打折后应付: " + context.calcPrice(1000)); 14 15 //普通会员计算商品价格 16 NormalMember normalMember = new NormalMember(); 17 context.setPriceStrategy(normalMember); 18 System.out.println("普通会员购买1000元商品,打折后应付: " + context.calcPrice(1000)); 19 } 20 }
运行结果:
金牌会员购买1000元商品,打折后应付: 800.0
银牌会员购买1000元商品,打折后应付: 900.0
普通会员购买1000元商品,打折后应付: 950.0
策略模式讲解
1.策略模式的功能: 策略模式是把具体的算法实现从具体的业务处理中独立出来,把它们实现成为单独的算法类,从而形成一系列的算法,并让这些算法可以相互替换。(当程序里面存在多个 if/else 形式的代码,而这些 if/else 又需要经常变动的时候就可以考虑选择策略模式来解决问题,把每个if/else 都单独出来形成算法类。)
2.策略模式的重心不是如何实现算法,而是如何组织、调用这些算法,从而让程序结构更灵活,具有更好的维护性和扩展性。(程序中的多个if/else语句表达的是一个平等的功能结构,根据业务条件 要么执行if,要么执行else,或者执行elseif,而策略模式就是把各个平等的具体实现封装到单独的策略实现类中,然后通过上下文与具体的策略类进行交互。)
3.策略算法是相同行为的不同实现,用我们自己的话说就是 "见人说人话,见鬼说鬼话", 同样一件事情面对不同的人有不同的说法。
4.策略模式调用顺序图:
(a)客户端选择并创建具体的策略对象
(b)然后客户端创建上下文
(c)客户端调用上下文的方法来执行功能,在调用的时候,从客户端传入策略算法需要的参数
(d)上下文接到客户端的调用请求,会把这个请求转发给它持有的具体策略类
5.策略模式的优缺点
优点:
(a)策略模式定义了一系列的算法,这些算法可以相互替换,避免了多重的if/else语句
(b)方便程序的扩展
缺点:
(a)客户端必须知道全部的具体策略类
(b)如果业务分支很多,会创建大量的具体策略类
6.策略模式的本质: 分离算法, 选择实现。
(PS:读 "研磨设计模式"一书后的总结)