一、策略模式概述
首先给出一个听起来官方的定义:策略模式将可变的部分从程序中抽象分离成算法接口,在该算法接口下分别封装一系列算法实现。
举一个实际的例子来说明:我们在网上购物支付的时候,会发现电商平台一般会提供各种各样的支付方式选择,用户只需要选择其中之一便可进行支付操作。
可以发现,上述例子中的支付算法便是抽象出来的算法接口,在这个接口里面封装了各种支付算法的实现,比如工商银行支付、支付宝支付……等等。这样,当需要添加一种新的支付方式时,只需要在接口下提供一个新的算法实现即可,客户端可以独立于这种改变之外。可以看到策略模式对于新增的需求具有弹性的支持。
二、以一个实例说明策略模式的实现
我们用文章开篇的用户网购支付的例子来说明策略模式。首先有如下基本代码,包括一个抽象顾客基类Consumer.java和两个顾客类。
Consumer.java:
package com.bebdong.Strategy_pattern;
/**
* @author Administrator
* 用户的抽象类
*/
public abstract class Consumer {
public void login(){
System.out.println("用户登录");
}
public void buy(){
System.out.println("用户购买");
}
public abstract void display();
}
ConsumerA.java:
package com.bebdong.Strategy_pattern;
public class ConsumerA extends Consumer {
public ConsumerA() {
super();
}
@Override
public void display() {
System.out.println("我是顾客类型A……");
}
}
ConsumerB.java:
package com.bebdong.Strategy_pattern;
public class ConsumerB extends Consumer {
public ConsumerB() {
super();
}
@Override
public void display() {
System.out.println("我是顾客类型B……");
}
}
OK,有了上面的基础,我们来讨论如何实现支付这样的一个行为:
- 继承:类似buy()方法和login()方法,在抽象基类中实现一个pay()来实现支付。虽然这样简(cu)单(bao),但是也具有相当的缺点:那就是不够灵活。因为不同的顾客会偏好不同的支付方式。
- 抽象方法(多态):类似dispaly()方法,在抽象基类中添加一个抽象方法pay(),强迫子类来实现它。这样一来,好像解决了第一个方案的问题,但是我们仔细思考可以发现问题:对未来需求的弹性不高,一个用户可能拥有几种支付方式,而且不利于代码维护。试想一下,如果某天我们需要改变这个支付方法的名称(可能不恰当,只是举个例子说明),那么每一个顾客类型的代码都需要修改。
- 组合。
经过上面的分析,我们说继承是面向对象编程中代码重用的一门利器,但有时候它也有不可避免的缺点。在《Effective Java》一书中有这样能一句话:Favor composition over inheritance. 意思大致为组合优于继承。那么什么是组合呢?
在类中增加一个私有域,引用另一个已有类的实例,通过调用引用实例的方法从而获得某种,这种设计被称之为组合(复合)。
利用组合和策略模式的思想,我们得出了如下方案:添加一个支付的接口PayWay,然后添加不同的支付方式类来实现这样一个接口。
PayWay.java(将共性抽象为接口)
package com.bebdong.Strategy_pattern;
public interface PayWay {
public void performPay();
}
Consumer.java:做相应修改(对象类拥有策略接口)
package com.bebdong.Strategy_pattern;
/**
* @author Administrator
* 用户的抽象类
*/
public abstract class Consumer {
public void login(){
System.out.println("用户登录");
}
public void buy(){
System.out.println("用户购买");
}
public abstract void display();
/*以下部分为新增代码*/
private PayWay payWay;
public void setPayWay(PayWay payWay){
this.payWay=payWay;
}
public void pay(){
payWay.performPay();
}
}
下面我们封装两种支付方式以具体说明:(添加策略实现类)
AliPay.java:
package com.bebdong.Strategy_pattern;
public class AliPay implements PayWay {
@Override
public void performPay() {
System.out.println("用支付宝支付");
}
}
ICBCPay.java:
package com.bebdong.Strategy_pattern;
public class ICBCPay implements PayWay {
@Override
public void performPay() {
System.out.println("用工行支付");
}
}
假设用户类型A使用支付宝支付,而用户类型B使用工行支付(为不同对象注入不同的策略)
下面我们用ConsumerTest.java来模拟这个过程:
package com.bebdong.Strategy_pattern;
public class ConsumerTest {
public static void main(String[] args) {
System.out.println("====模拟开始====");
Consumer consumerA=new ConsumerA();
Consumer consumerB=new ConsumerB();
consumerA.setPayWay(new AliPay());
consumerB.setPayWay(new ICBCPay());
System.out.println(">>>>这里是用户A<<<<");
consumerA.display();
consumerA.login();
consumerA.buy();
consumerA.pay();
System.out.println(">>>>这里是用户B<<<<");
consumerB.display();
consumerB.login();
consumerB.buy();
consumerB.pay();
System.out.println("====模拟结束====");
}
}
运行可以得到如下结果:
这样一来,当需要增加新的支付方式时,只需要继承PayWay接口并提供具体实现即可。所有顾客都可以选择使用这种方式进行支付。
三、总结
从上面的实例中,我们可以概括出策略模式的设计原则:
- 1、将应用中不变的部分抽象为接口,而变得部分交由具体实现;
- 2、面向接口编程,而不是面向实现编程。接口规定了实现框架,然后通过多态实现了多样性;
- 3、组合优先继承。
>>>>>>>>>策略模式的实现要点<<<<<<<<<
- 1、通过分析,分理处策略接口;
- 2、为接口提供实现类;
3、对象“拥有”一个Strategy,通过它来实现具体的行为;
4、客户程序中选择/组装正确的Strategy实现。
>>>>>>>>>策略模式的优点<<<<<<<<<
- 1、策略模式往往和组合密不可分,架构具有相当的灵活性;
- 2、负有弹性,能较好的应对需求变化(开-闭);
- 3、拥有更好的代码复用性
- 4、消除了相当多的条件语句(不需要判断要执行哪种行为),易于维护。
>>>>>>>>>策略模式的缺点<<<<<<<<<
- 1、可以看到需要为每一个类(对象)注入策略(要点4),故而客户程序需要了解策略的实现细节。
- 2、随着时间推移,策略实现类将显著增加。
>>>>>>>>>策略模式的使用场景<<<<<<<<<
- 许多相关类仅仅是行为差异,将差异共性抽象为策略接口;
- 运行时选取不同的算法变体。如不同的支付算法,运行时只会运行一种支付算法。
- 程序含有相当的条件判断语句if……else……。