在软件开发中,我们经常需要实现一些算法或策略,而这些算法可能会随着需求的变化而改变。策略模式就是为了应对这种情况而生的,它提供了一种灵活且可扩展的方式来管理和切换不同的算法。
策略模式(Strategy Pattern)是一种行为型设计模式,它定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化独立于使用算法的客户端。
策略模式主要包含以下三个角色:
一个电商系统,需要根据不同的会员等级提供不同的折扣策略。
首先,定义一个折扣策略接口:
public interface DiscountStrategy {
double applyDiscount(double price);
}
然后,实现几个具体的折扣策略:
// 普通会员折扣
public class RegularDiscountStrategy implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price * 0.95; // 5%折扣
}
}
// 黄金会员折扣
public class GoldDiscountStrategy implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price * 0.9; // 10%折扣
}
}
// 白金会员折扣
public class PlatinumDiscountStrategy implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price * 0.85; // 15%折扣
}
}
接下来,创建一个上下文类来使用这些策略:
public class ShoppingCart {
private DiscountStrategy discountStrategy;
public void setDiscountStrategy(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public double checkout(double price) {
if (discountStrategy == null) {
return price;
}
return discountStrategy.applyDiscount(price);
}
}
最后,可以在客户端代码中使用这个购物车ShoppingCart :
public class Client {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// 普通会员购物
cart.setDiscountStrategy(new RegularDiscountStrategy());
System.out.println("Regular member's price: " + cart.checkout(100));
// 黄金会员购物
cart.setDiscountStrategy(new GoldDiscountStrategy());
System.out.println("Gold member's price: " + cart.checkout(100));
// 白金会员购物
cart.setDiscountStrategy(new PlatinumDiscountStrategy());
System.out.println("Platinum member's price: " + cart.checkout(100));
}
}
Java标准库中有很多使用策略模式的例子。
Comparator
接口是Java中最常见的策略模式实现之一。它允许我们定义对象的自定义排序策略。
import java.util.*;
public class ComparatorExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("John", "Alice", "Bob", "Charlie");
// 使用自然顺序(字母顺序)
Collections.sort(names);
System.out.println("Natural order: " + names);
// 使用自定义比较器(按长度排序)
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String s1, String s2) {
return s1.length() - s2.length();
}
});
System.out.println("Sorted by length: " + names);
// 使用Lambda表达式(按长度降序排序)
Collections.sort(names, (s1, s2) -> s2.length() - s1.length());
System.out.println("Sorted by length (descending): " + names);
}
}
在这个例子中,Comparator
就是策略接口,不同的排序方法是具体的策略实现。
ThreadPoolExecutor
类使用策略模式来决定如何处理新提交的任务,特别是当线程池已满时。
import java.util.concurrent.*;
public class ThreadPoolExample {
public static void main(String[] args) {
// 使用CallerRunsPolicy
ThreadPoolExecutor executorWithCallerRuns = new ThreadPoolExecutor(
2, 2, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(2),
new ThreadPoolExecutor.CallerRunsPolicy());
// 使用DiscardPolicy
ThreadPoolExecutor executorWithDiscard = new ThreadPoolExecutor(
2, 2, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(2),
new ThreadPoolExecutor.DiscardPolicy());
// 提交任务
for (int i = 0; i < 10; i++) {
final int taskId = i;
executorWithCallerRuns.execute(() -> {
System.out.println("Task " + taskId + " executed by " +
Thread.currentThread().getName());
});
}
executorWithCallerRuns.shutdown();
executorWithDiscard.shutdown();
}
}
这里,RejectedExecutionHandler
接口是策略接口,CallerRunsPolicy
和DiscardPolicy
是具体的策略实现。
虽然不是严格意义上的策略模式,HttpServlet
类使用了类似策略模式的方法来处理不同类型的HTTP请求。
import javax.servlet.http.*;
import java.io.IOException;
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.getWriter().println("GET request handled");
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws IOException {
response.getWriter().println("POST request handled");
}
// 其他HTTP方法...
}
在这个例子中,不同的HTTP方法(GET、POST等)可以看作是不同的策略。
InputStream
类及其子类使用了策略模式来实现不同的读取策略。
import java.io.*;
public class InputStreamExample {
public static void main(String[] args) throws IOException {
// 文件输入流策略
try (InputStream fileIn = new FileInputStream("example.txt")) {
int data = fileIn.read();
while(data != -1) {
System.out.print((char) data);
data = fileIn.read();
}
}
// 字节数组输入流策略
byte[] bytes = "Hello, World!".getBytes();
try (InputStream byteArrayIn = new ByteArrayInputStream(bytes)) {
int data = byteArrayIn.read();
while(data != -1) {
System.out.print((char) data);
data = byteArrayIn.read();
}
}
}
}
这里,InputStream
是抽象策略,FileInputStream
和ByteArrayInputStream
是具体策略。
Swing库中的LayoutManager
接口使用策略模式来定义不同的布局策略。
import javax.swing.*;
import java.awt.*;
public class LayoutExample {
public static void main(String[] args) {
JFrame frame = new JFrame("Layout Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// 使用FlowLayout策略
JPanel flowPanel = new JPanel(new FlowLayout());
flowPanel.add(new JButton("Button 1"));
flowPanel.add(new JButton("Button 2"));
// 使用BorderLayout策略
JPanel borderPanel = new JPanel(new BorderLayout());
borderPanel.add(new JButton("North"), BorderLayout.NORTH);
borderPanel.add(new JButton("South"), BorderLayout.SOUTH);
frame.add(flowPanel, BorderLayout.NORTH);
frame.add(borderPanel, BorderLayout.CENTER);
frame.pack();
frame.setVisible(true);
}
}
在这个例子中,LayoutManager
是策略接口,FlowLayout
和BorderLayout
是具体的布局策略。
策略模式是一种非常实用的设计模式,它提供了管理算法族、分离算法实现和算法使用的有效方法。通过策略模式,可以在不修改原有系统的情况下,灵活地增加新的算法或行为。在实际开发中,应该根据具体情况来判断是否使用策略模式,以达到代码复用、增强扩展性和维护性的目的。