概述
策略模式是一种行为设计模式, 它能让你定义一系列算法, 并将每种算法分别放入独立的类中, 以使算法的对象能够相互替换
。
主要目的是通过定义相似的算法,替换if else 语句写法
,并且可以随时相互替换
结构

示例
策略模式在 Java 代码中很常见。 它经常在各种框架中使用, 能在不扩展类的情况下向用户提供改变其行为的方式。
javax.servlet.http.HttpServlet: service()方法, 还有所有接受 HttpServletRequest和 HttpServletResponse对象作为参数的 doXXX()方法。
识别方法: 策略模式可以 通过允许嵌套对象完成实际工作的方法,以及允许将该对象替换为不同对象的设置器来识别。
伪代码实现
strategies
策略(strategies)的定义:所有具体策略的通用接口, 它声明了一个上下文用于执行策略的方法
java
public interface PayStrategy {
boolean pay(int paymentAmount);
void collectPaymentDetails();
}
Concrete Strategies
具体策略 (Concrete Strategies): 实现上下文所用算法的各种不同变体。
PayByPayPal: 使用 PayPal 支付
java
public class PayByPayPal implements PayStrategy {
@Override
public void collectPaymentDetails() {
// todo
}
@Override
public boolean pay(int paymentAmount) {
// todo
}
PayByCreditCard: 使用信用卡支付
java
public class PayByCreditCard implements PayStrategy {
@Override
public void collectPaymentDetails() {
// todo
}
@Override
public boolean pay(int paymentAmount) {
// todo
}
Context
上下文 (Context): 维护指向具体策略的引用, 且仅通过策略接口与该对象进行交流。
java
public class Order {
private int totalCost = 0;
private boolean isClosed = false;
// 提供一个计算的接口供客户端使用。
public void processOrder(PayStrategy strategy) {
strategy.collectPaymentDetails();
// Here we could collect and store payment data from the strategy.
}
}
Client
客户端 (Client) 会创建一个特定策略对象并将其传递给上下文。
上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。
java
public class Client {
private static Map<Integer, Integer> priceOnProducts = new HashMap<>();
private static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
private static Order order = new Order();
private static PayStrategy strategy;
static {
priceOnProducts.put(1, 2200);
priceOnProducts.put(2, 1850);
priceOnProducts.put(3, 1100);
priceOnProducts.put(4, 890);
}
public static void main(String[] args) throws IOException {
while (!order.isClosed()) {
int cost;
String continueChoice;
do {
System.out.print("Please, select a product:" + "\n" +
"1 - Mother board" + "\n" +
"2 - CPU" + "\n" +
"3 - HDD" + "\n" +
"4 - Memory" + "\n");
int choice = Integer.parseInt(reader.readLine());
cost = priceOnProducts.get(choice);
System.out.print("Count: ");
int count = Integer.parseInt(reader.readLine());
order.setTotalCost(cost * count);
System.out.print("Do you wish to continue selecting products? Y/N: ");
continueChoice = reader.readLine();
} while (continueChoice.equalsIgnoreCase("Y"));
if (strategy == null) {
System.out.println("Please, select a payment method:" + "\n" +
"1 - PalPay" + "\n" +
"2 - Credit Card");
String paymentMethod = reader.readLine();
// Client creates different strategies based on input from user,
// application configuration, etc.
if (paymentMethod.equals("1")) {
strategy = new PayByPayPal();
} else {
strategy = new PayByCreditCard();
}
}
// Order object delegates gathering payment data to strategy object,
// since only strategies know what data they need to process a
// payment.
order.processOrder(strategy);
System.out.print("Pay " + order.getTotalCost() + " units or Continue shopping? P/C: ");
String proceed = reader.readLine();
if (proceed.equalsIgnoreCase("P")) {
// Finally, strategy handles the payment.
if (strategy.pay(order.getTotalCost())) {
System.out.println("Payment has been successful.");
} else {
System.out.println("FAIL! Please, check your data.");
}
order.setClosed();
}
}
}
}
应用场景
当你想使用对象中各种不同的算法变体, 并希望能在运行时切换算法时
, 可使用策略模式。
策略模式让你能够将对象关联至可以不同方式执行特定子任务的不同子对象, 从而以间接方式在运行时更改对象行为。
当你有许多仅在执行某些行为时略有不同的相似类时
, 可使用策略模式。
策略模式让你能将不同行为抽取到一个独立类层次结构中, 并将原始类组合成同一个, 从而减少重复代码。
如果算法在上下文的逻辑中不是特别重要, 使用该模式能将类的业务逻辑与其算法实现细节隔离开来。
策略模式让你
能将各种算法的代码、 内部数据和依赖关系与其他代码隔离开来
。 不同客户端可通过一个简单接口执行算法, 并能在运行时进行切换。
当类中使用了复杂条件运算符以在同一算法的不同变体中切换时, 可使用该模式。
策略模式将所有继承自同样接口的算法抽取到独立类中, 因此不再需要条件语句。 原始对象并不实现所有算法的变体, 而是将执行工作委派给其中的一个独立算法对象。
实现步骤
首先,从上下文类中找出修改频率较高的算法
其次,声明该算法所有变体的通用策略接口。将算法逐一抽取到各自的类中, 它们都必须实现策略接口。
之后,在上下文类中添加一个成员变量用于保存对于策略对象的引用
。
- 然后提供设置器以修改该成员变量。
- 上下文仅可通过策略接口同策略对象进行交互,
- 如有需要还可定义一个接口来让策略访问其数据。
最后,客户端必须将上下文类与相应策略进行关联, 使上下文可以预期的方式完成其主要工作。
优缺点
策略模式优点:
- 扩展性好,可以在不修改对象结构的情况下,为新的算法进行添加新的类进行实现;
- 灵活性好,可以对算法进行自由切换;
策略模式缺点:
- 使用策略类变多,会增加系统的复杂度。;
- 客户端必须知道所有的策略类才能进行调用;