文章目录
- 策略模板模式
- 1.策略模式的本质
- 2.何时选用策略模式
- 3.优缺点
- 4.策略模式的结构
- 5.实现
- 支付案例
- 支付案例参数不一致问题
策略模板模式
1.策略模式的本质
策略模式的本质:分离算法,选择实现。
纵观整个策略模式实现的功能和设计,它的本质还是“分离算法,选择实现”,因为分离并封装了算法,才能够很容易地修改和添加算法;也能很容易地动态切换使用不同的算法,也就是动态选择一个算法来实现需要的功能。
2.何时选用策略模式
建议在以下情况中选用策略模式。
- 出现有许多相关的类,仅仅是行为有差别的情况下,可以使用策略模式来使用多个行为中的一个来配置一个类的方法,实现算法动态切换。
- 出现同一个算法,有很多不同实现的情况下,可以使用策略模式来把这些“不同的实现”实现成为一个算法的类层次。
- 需要封装算法中,有与算法相关数据的情况下,可以使用策略模式来避免暴露这些跟算法相关的数据结构。
- 出现抽象一个定义了很多行为的类,并且是通过多个if-else语句来选择这些行为的情况下,可以使用策略模式来代替这些条件语句。
3.优缺点
策略模式有以下优点。
-
定义一系列算法
策略模式的功能就是定义一系列算法,实现让这些算法可以相互替换。所以会为这一系列算法定义公共的接口,以约束一系列算法要实现的功能。如果这一系列算法具有公共功能,可以把策略接口实现成为抽象类,把这些公共功能实现到父类中,对于这个问题,前面讲了三种处理方法,这里就不再啰嗦了。 -
避免多重条件语句
根据前面的示例会发现,策略模式的一系列策略算法是平等的,是可以互换的,写在一起就是通过if-else结构来组织,如果此时具体的算法实现中又有条件语句,就构成了多重条件语句,使用策略模式能避免这样的多重条件语句。 -
更好的扩展性
在策略模式中扩展新的策略实现非常容易,只要增加新的策略实现类,然后在使用策略的地方选择使用这个新的策略实现就可以了。
策略模式有以下缺点。
-
客户必须了解每种策略的不同
策略模式也有缺点,比如让客户端来选择具体使用哪一个策略,这就需要客户了解所有的策略,还要了解各种策略的功能和不同,这样才能做出正确的选择,而且这样也暴露了策略的具体实现。 -
增加了对象数目
由于策略模式把每个具体的策略实现都单独封装成为类,如果备选的策略很多的话,那么对象的数目就会很可观。 -
只适合扁平的算法结构
策略模式的一系列算法地位是平等的,是可以相互替换的,事实上构成了一个扁平的算法结构,也就是在一个策略接口下,有多个平等的策略算法,就相当于兄弟算法。而且在运行时刻只有一个算法被使用,这就限制了算法使用的层级,使用的时候不能嵌套使用。
对于出现需要嵌套使用多个算法的情况,比如折上折、折后返卷等业务的实现,需要组合或者是嵌套使用多个算法的情况,可以考虑使用装饰模式,或是变形的职责链,或是AOP等方式来实现。
4.策略模式的结构
- Strategy:策略接口,用来约束一系列具体的策略算法。Context使用这个接口来调用具体的策略实现定义的算法。
- ConcreteStrategy:具体的策略实现,也就是具体的算法实现。
- Context:上下文,负责和具体的策略类交互。通常上下文会持有一个真正的策略实现,上下文还可以让具体的策略类来获取上下文的数据,甚至让具体的策略类来回调上下文的方法。
5.实现
支付案例
策略模式在工作中使用,一下就能想到的就是支付
模拟支付宝、微信、银联支付
支付策略及其实现类
/**
* @description:支付策略接口
*/
public interface PayStrategy {
/**
* 支付方法
* @param orderId 订单id
* @param amount 金额
* @return
*/
boolean pay(String orderId, Long amount);
}
/**
* @description:微信支付策略
*/
public class WxPay implements PayStrategy{
@Override
public boolean pay(String orderId, Long amount) {
System.out.println("调用微信支付策略-->订单id:"+orderId+" ,金额:"+amount);
return true;
}
}
/**
* @description:支付宝支付策略
*/
public class ZfbPay implements PayStrategy{
@Override
public boolean pay(String orderId, Long amount) {
System.out.println("调用支付宝支付策略-->订单id:"+orderId+" ,金额:"+amount);
return true;
}
}
/**
* @description:银联支付策略
*/
public class YlPay implements PayStrategy{
@Override
public boolean pay(String orderId, Long amount) {
System.out.println("调用银联支付策略-->订单id:"+orderId+" ,金额:"+amount);
return true;
}
}
支付上下文类
/**
* @description:支付上下文类
*/
public class PayContext {
/**
* 订单id
*/
private String orderId;
/**
* 金额
*/
private Long amount;
private PayStrategy payStrategy;
public PayContext(String orderId, Long amount, PayStrategy payStrategy) {
this.orderId = orderId;
this.amount = amount;
this.payStrategy = payStrategy;
}
public void payContext(){
//调用具体策略的支付
this.payStrategy.pay(orderId,amount);
}
}
测试类
public class Client {
public static void main(String[] args) {
//微信支付
new PayContext("10001",100L,new WxPay()).payContext();
//支付宝支付
new PayContext("10002",200L,new ZfbPay()).payContext();
//银联支付
new PayContext("10003",300L,new YlPay()).payContext();
}
}
结果
看起来好像没有上下文什么事情,但是如果没有上下文,那么就需要客户端来直接与具体的策略交互,尤其是当需要提供一些公共功能,或者是相关状态存储的时候,会大大增加客户端使用的难度。因此,引入上下文还是很必要的,有了上下文,这些工作就由上下文来完成了,客户端只需要与上下文交互就可以了,这样会让整个设计模式更独立、更有整体性,也让客户端更简单。
支付案例参数不一致问题
实际使用各种支付时,各自的参数必然不一样,但是策略又是接口定义好的,那应该怎么办呢,这时介于客户端和策略方法中间的上下文类就发挥作用了,可以在这里面搞点小动作,把微信和支付宝用到的参数声明在这里,然后把自己传给策略类,不同的策略类需要什么参数就取什么参数
策略类及其实现类
/**
* @description:支付策略接口
*/
public interface PayStrategy {
/**
* 支付方法
* @return
*/
boolean pay(PayContext payContext);
}
/**
* @description:微信支付策略
*/
public class WxPay implements PayStrategy{
@Override
public boolean pay(PayContext payContext) {
System.out.println("微信支付参数:"+payContext.getWxAppserect());
System.out.println("调用微信支付策略-->订单id:"+payContext.getOrderId()+" ,金额:"+payContext.getAmount());
return true;
}
}
/**
* @description:支付宝支付策略
*/
public class ZfbPay implements PayStrategy{
@Override
public boolean pay(PayContext payContext) {
System.out.println("支付宝支付参数:"+payContext.getZfbId());
System.out.println("调用支付宝支付策略-->订单id:"+payContext.getOrderId()+" ,金额:"+payContext.getAmount());
return true;
}
}
/**
* @description:银联支付策略
*/
public class YlPay implements PayStrategy{
@Override
public boolean pay(PayContext payContext) {
System.out.println("银联支付没有参数");
System.out.println("调用银联支付策略-->订单id:"+payContext.getOrderId()+" ,金额:"+payContext.getAmount());
return true;
}
}
支付上下文类
/**
* @description:支付上下文类
*/
@Getter
public class PayContext {
/**
* 订单id
*/
private String orderId;
/**
* 金额
*/
private Long amount;
/**
* 微信支付需要的参数
*/
private String wxAppserect;
/**
* 支付宝支付需要的参数
*/
private String zfbId;
private PayStrategy payStrategy;
public PayContext(String orderId, Long amount, String wxAppserect, String zfbId, PayStrategy payStrategy) {
this.orderId = orderId;
this.amount = amount;
this.wxAppserect = wxAppserect;
this.zfbId = zfbId;
this.payStrategy = payStrategy;
}
public void payContext(){
//调用具体策略的支付
this.payStrategy.pay(this);
}
}
测试类
public class Client {
public static void main(String[] args) {
//微信支付
PayContext wxPay = new PayContext("10001", 100L, "wx123", "", new WxPay());
wxPay.payContext();
//支付宝支付
PayContext zfbPay = new PayContext("10001", 100L, "", "zfb123", new ZfbPay());
zfbPay.payContext();
//银联支付
PayContext ylPay = new PayContext("10001", 100L, "", "", new YlPay());
ylPay.payContext();
}
}
结果