《设计模式》这本对策略模式的定义是:定义一组算法,将每个算法都封装起来,是他们之间可以相互替换。即针对一组算法,将每个算法都封装到具有共同接口的类中,使他们可以互相相互替换,在不影响客户端的情况下发生变化。
策略模式的三个角色:
1、环境角色(Context):承启上下文的作用,防止对策略的直接访问
2、抽象策略(Strategy):对策略进行抽象,定义每个策略必须具有的方法和属性
3、具体策略(Concrete Strategy):实现策略的具体操作
接下来我们通过一个具体的应用去看看吧。
1、定义一个环境角色(Context):
public class Context {
//持有一个具体策略的对象
private Strategy strategy;
/**
* 构造函数,传入一个具体策略对象
*/
public Context(Strategy strategy){
this.strategy = strategy;
}
/**
* 策略方法
*/
public double shoppingPay(double price){
return strategy.shoppingPay(price);
}
}
2、定义一个抽象策略类(Strategy),里面定义一个模拟购物付款的接口。
public interface Strategy {
double shoppingPay(double price);
}
3、对于购物付款的方式,我们都知道有微信支付,支付宝,银联等方式,那下面就针对这三种方式定义具体付款策略(Concrete Strategy),且实现策略类抽象策略类(Strategy)接口。
//微信支付
public class WeChatPay implements Strategy {
@Override
public double shoppingPay(double price) {
System.out.println("微信支付,每单优惠20元");
return price-20;
}
}
//支付宝支付
public class AliPay implements Strategy {
@Override
public double shoppingPay(double price) {
System.out.println("支付宝支付,打8折");
return price*0.8;
}
}
//银联支付
public class UnionPay implements Strategy {
@Override
public double shoppingPay(double price) {
System.out.println("银联支付,没有优惠");
return price;
}
}
public static void main(String[] args) {
Strategy strategy=new WeChatPay();
Context context=new Context(strategy);
double pay=context.shoppingPay(500);
System.out.println("实际付款"+pay);
}
//执行输出
微信支付,每单优惠20元
实际付款480.0
public static void main(String[] args) {
Strategy strategy=new AliPay();
Context context=new Context(strategy);
double pay=context.shoppingPay(500);
System.out.println("实际付款"+pay);
}
//执行输出
支付宝支付,打8折
实际付款400.0
策略模式是将使用的方法分别封装成独立的类,然后使用统一的接口管理,让客户端能够随时调用他们,并且不同的行为类之间可以相互替换。
比如有一天我们忘了带银行卡,手机也没电了,那就只能现金付款了,结合以上的案例我们只需要添加一个现金付款的实例类即可,代码如下:
public class CashPay implements Strategy {
@Override
public double shoppingPay(double price) {
System.out.println("现金付款,获得一次抽奖机会");
return price;
}
}
测试代码如下:
public static void main(String[] args) {
Strategy strategy=new CashPay();
Context context=new Context(strategy);
double pay=context.shoppingPay(500);
System.out.println("实际付款"+pay);
}
测试输出如下:
现金付款,获得一次抽奖机会
实际付款500.0
通过以上的扩展可以看出,应用策略模式对具体的策略进行扩展很容易,不需要改变原来的代码,但缺点是客户端调用必须知道所有的策略类的具体实现。
下面我们对上面的实例进行优化,
1、 定义一个枚举类,封装所有的支付方法
public enum PayTypeEnum {
CASH("现金"), ALIPAY("支付宝"), WECAHTPAY("微信"), UNIONPAY("银联");
private String payType;
// 枚举类型的构造函数默认为private
private PayTypeEnum(String payType) {
this.payType = payType;
}
public String getPayType() {
return payType;
}
}
2、定义一个策略工厂,封装支付方式与策略类的关系,并且通过static代码块进行预加载
public class StrategyFactory {
private static StrategyFactory factory = new StrategyFactory();
private static Map<String, Strategy> map = new HashMap<>();
private StrategyFactory() {
}
static {
map.put(PayTypeEnum.CASH.name(), new CashPay());
map.put(PayTypeEnum.ALIPAY.name(), new AliPay());
map.put(PayTypeEnum.WECAHTPAY.name(), new WeChatPay());
map.put(PayTypeEnum.UNIONPAY.name(), new UnionPay());
}
public Strategy getStrategy(String type) {
return map.get(type);
}
public static StrategyFactory getInstance() {
return factory;
}
}
Strategy类修改如下:
public interface Strategy {
double shoppingPay(double price, String type);
}
Context类修改如下:
public class Context {
// 持有一个具体策略的对象
private Strategy strategy;
/**
* 通过对应的payType从策略工厂获取对应的策略实现类
*/
public double shoppingPay(double price, String payType) {
strategy = StrategyFactory.getInstance().getStrategy(payType);
return strategy.shoppingPay(price, payType);
}
public Strategy getStrategy() {
return strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
}
接口改变,对应的策略实现类也要做相应的改动,具体改动如下:
public class WeChatPay implements Strategy {
@Override
public double shoppingPay(double price, String type) {
System.out.println(PayTypeEnum.WECAHTPAY.getPayType()+"支付,每单优惠20元");
return price - 20;
}
}
public class UnionPay implements Strategy {
@Override
public double shoppingPay(double price, String type) {
System.out.println(PayTypeEnum.UNIONPAY.getPayType()+"支付,没有优惠");
return price;
}
}
public class AliPay implements Strategy {
@Override
public double shoppingPay(double price, String type) {
System.out.println(PayTypeEnum.ALIPAY.getPayType()+"支付,打8折");
return price * 0.8;
}
}
public class CashPay implements Strategy {
@Override
public double shoppingPay(double price, String type) {
System.out.println(PayTypeEnum.CASH.getPayType()+"付款,获得一次抽奖机会");
return price;
}
}
对以上4个实现类进行测试,代码如下:
public static void main(String[] args) {
Context context = new Context();
double alipay = context.shoppingPay(500, PayTypeEnum.ALIPAY.name());
System.out.println("实际付款" + alipay);
double wxpay = context.shoppingPay(500, PayTypeEnum.WECAHTPAY.name());
System.out.println("实际付款" + wxpay);
double unpay = context.shoppingPay(500, PayTypeEnum.UNIONPAY.name());
System.out.println("实际付款" + unpay);
double cashpay = context.shoppingPay(500, PayTypeEnum.CASH.name());
System.out.println("实际付款" + cashpay);
}
//执行打印输出
支付宝支付,打8折
实际付款400.0
微信支付,每单优惠20元
实际付款480.0
银联支付,没有优惠
实际付款500.0
现金付款,获得一次抽奖机会
实际付款500.0
以上代码结构如下,可直接运行: