1. 策略模式定义
策略模式(Strategy):它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
策略模式封装了变化,定义一系列的算法,把它们一个个封装起来, 并且使它们可相互替换。策略模式就是用来封装算法的,可以用它来封装几乎任何类型的规则,只要遇到需要在不同时间应用不同的业务规则,就可以考虑使用策略模式处理这种变化的可能性。避免了在有多种算法相似的情况下,使用 if...else 所带来的复杂和难以维护。
2. 策略模式结构

-
Strategy 类:定义所有支持的算法的公共接口:
javapublic abstract class Strategy{ public abstract void algorithmInterface(); }
-
ConcreteStrategy 类:封装了具体的算法,继承Strategy 类:
javaclass ConcreteStrategyA extends Strategy { public void algorithmInterface(){ sout("算法A实现"); } } class ConcreteStrategyB extends Strategy { public void algorithmInterface(){ sout("算法B实现"); } } class ConcreteStrategyC extends Strategy { public void algorithmInterface(){ sout("算法C实现"); } }
-
Context 类:用一个 ConcreteStrategy 来配置,维护一个 Strategy 对象的引用:
javaclass Context { Strategy strategy; public Context(Strategy strategy){ this.strategy = strategu; } public void contextInterface(){ strategy.algorithmInterface(); } }
客户端代码调用:
java
Context context;
context = new Context(new ConcreteStrategyA());
context.contextInterface();
context = new Context(new ConcreteStrategyB());
context.contextInterface();
context = new Context(new ConcreteStrategyC());
context.contextInterface();
3. 策略模式举例
3.1 问题背景
设计一个程序,通过不同的方式进行支付。
很容易想到,先设计一个支付类:
java
public class PaymentMethod {
public static void pay(String paymentType, int amount){
switch(paymentType){
case "alipay":
sout("使用支付宝进行支付 " + amount +" 元");
break;
case "wechatpay":
sout("使用微信进行支付 " + amount +" 元");
break;
}
}
}
客户端代码:
java
Scanner sc = new Scanner(System.in);
sout("请输入支付方式(alipay、wechatpay):");
String paymentType = sc.nextLine();
sout("请输入支付金额:");
double amount = sc.nextLine();
PaymentMethod.pay(paymentType, amount);
3.2 简单工厂模式实现
如果想要增加或者修改支付方式,此时必须修改 PaymentMethod 类,这时可能就会影响到其他代码,可能不小心将其他代码进行了修改。应该将不同的支付方式进行分离,修改一个而不影响另外几个,增加支付方式也不影响其他代码。
使用简单工厂模式进行实现(点击阅读:简单工厂模式详解):
定义 Payment 支付接口:
java
interface PaymentMethod {
void pay(int amount);
}
支付宝、微信、信用卡支付类:
java
public class AliPay extends PaymentMethod{
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using AliPay");
}
}
public class WeChatPay extends PaymentMethod{
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using WeChatPay");
}
}
public class CreditCardPay implements PaymentMethod {
private String cardNumber;
public CreditCardPay(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(int amount) {
System.out.println("Paid " + amount + " using Credit Card ending with " + cardNumber);
}
}
工厂类:
java
public class PaymentFactory {
public static PaymentMethod createPayment(String paymentType,String cardNumber){
switch(paymentType.toLowerCase()) {
case "alipay":
return new AliPay();
break;
case "wechatpay":
return new WeChatPay();
break;
case "creditcardpay":
return new CreditCardPay(cardNumber);
break;
default:
throw new IllegalArgumentException("Invalid payment type: " + paymentType); }
}
}
}
客户端代码;
java
PaymentMethod paymentMethod = PaymentFactory.createPayment(paymentType, cardNumber);
paymentMethod.pay(100);
3.3 策略模式实现
使用简单工厂模式时,假设想要增加或者修改一个支付方式,需要增加一个类继承 PaymentMethod 类,并且需要在 PaymentFactory 中增加一个 case。每次改动都需要修改工厂。
其实支付方式都是一些算法,用工厂来生成算法对象,算法本身就是一种策略,这些算法随时都可能相互替换。
简单工厂模式中的 PaymentMethod 类就是抽象策略,支付宝、微信、信用卡支付类就是三个具体策略。
将简单工厂模式进行修改为策略模式:
java
public class PaymentMethodContext {
private PaymentMethod paymentMethod;
public PaymentMethodContext(PaymentMethod paymentMethod){
this.paymentMethod = paymentMethod;
}
public void pay(int amount){
return this.paymentMethod.pay(amount);
}
}
客户端代码:
java
PaymentMethodContext paymentMethodContext;
swith(paymentType.toLowerCase()){
case "alipay":
paymentMethodContext = new PaymentMethodContext(new AliPay());
break;
case "wechatpapy":
paymentMethodContext = new PaymentMethodContext(new WeChatPay());
break;
case "*":
paymentMethodContext = new PaymentMethodContext(new CreditCardPay());
break;
default:
throw new IllegalArgumentException("Invalid payment type: " + paymentType); }
}
paymentMethodContext.pay(100);
再看一次策略模式定义:
策略模式它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
策略模式关注的是算法的封装和替换,强调的是算法的多态性,让客户端能够选择不同的算法。
3.4 策略模式与简单工厂的结合
将判断的过程从客户端转移:
java
public class PaymentMethodContext {
private PaymentMethod paymentMethod;
public PaymentMethodContext(String paymentType, String creditCardNumber){
swith(paymentType.toLowerCase()){
case "alipay":
paymentMethod = new AliPay();
break;
case "wechatpapy":
paymentMethod = new WeChatPay();
break;
case "*":
paymentMethod = nnew CreditCardPay(creditCardNumber);
break;
default:
throw new IllegalArgumentException("Invalid payment type: " + paymentType); }
}
}
public void pay(int amount){
return this.paymentMethod.pay(amount);
}
}
客户端代码;
java
PaymentMethodContext paymentMethodContext = new PaymentMethodContext(paymentType, creditCardNumber);
paymentMethodContext.pay(amount);
简单工厂模式与策略模式客户端代码对比:
java
// 简单工厂模式
PaymentMethod paymentMethod = PaymentFactory.createPayment(paymentType, cardNumber);
paymentMethod.pay(100);
// 策略模式
PaymentMethodContext paymentMethodContext = new PaymentMethodContext(paymentType, creditCardNumber);
paymentMethodContext.pay(amount);
对于简单工厂模式,客户端需要认识两个类 PaymentFactory 和 PaymentMethod。
对于策略模式和简单工厂结合:客户端只需认识一个类 PaymentMethodContext。耦合度更低,具体的算法彻底地与客户端分离。
策略模式和简单工厂模式的应用场景:
-
策略模式:主要用于使算法的变化独立于使用算法的客户。策略模式用于在运行时选择不同的算法变体。
如果需要根据不同情况使用不同的业务规则,那么策略模式更为合适。
-
简单工厂模式:目的在于创建对象。它将对象的创建封装起来,使得客户端在实例化对象时不需要知道具体的类。
如果需要根据一定的逻辑实例化不同的对象类,但不关心具体的实现细节,那么简单工厂模式更为合适。
4. 策略模式的优点和缺点
4.1 优点
- 分离关注点:策略模式将定义算法的代码从使用算法的代码中分离出去,符合单一职责原则。
- 提高可扩展性:可以在不修改客户代码的情况下引入新的策略。
- 替换灵活性:策略模式允许在运行时更改对象的行为。
- 复用策略:策略可以被多个上下文复用。
4.2 缺点
-
客户端需要了解不同策略:策略模式强制客户端选择一个具体策略,因此客户端需要了解每个策略的差异。
解决方法:可以通过使用一个配置文件或工厂模式来创建策略对象,将具体策略类的创建逻辑从客户端代码中抽离。
-
增加对象数量:每一个策略都是一个对象,如果策略过多会导致很多策略类。
解决方法:可以通过使用享元模式来缓存和复用策略对象,以减少实例创建。
-
协作策略问题:如果策略需要协作产生较大的数据结构交互,这将使策略使用变得复杂。
解决方法:可以通过引入中介类来封装那些交互,从而简化策略类之间的通信复杂性。