策略模式(Strategy Pattern)详解
文章目录
- 1. 什么是策略模式?
- 2. 为什么需要策略模式?
- 3. 策略模式的核心概念
- 3.1 策略(Strategy)
- 3.2 具体策略(Concrete Strategy)
- 3.3 上下文(Context)
- 4. 策略模式的结构
- 5. 策略模式的基本实现
- 5.1 基础示例:不同的支付策略
- 5.2 基础示例:不同的排序策略
- 6. 策略模式的高级实现
- 6.1 使用枚举实现策略模式
- 6.2 使用Lambda表达式实现策略模式
- 6.3 使用策略工厂
- 7. 策略模式在Java中的实际应用
- 7.1 Java Collections Framework中的排序
- 7.2 线程池中的拒绝策略
- 7.3 Spring框架中的策略模式
- 8. 策略模式与其他设计模式的比较
- 8.1 策略模式 vs 状态模式
- 8.2 策略模式 vs 命令模式
- 8.3 策略模式 vs 工厂模式
- 8.4 策略模式 vs 模板方法模式
- 9. 策略模式的优缺点
- 9.1 优点
- 9.2 缺点
- 10. 何时使用策略模式?
- 10.1 实际应用场景
- 10.2 Java中常见的策略模式应用
- 11. 常见问题与解决方案
- 11.1 策略选择问题
- 11.2 参数传递问题
- 11.3 策略组合问题
- 12. 策略模式的最佳实践
- 12.1 设计建议
- 12.2 代码示例:优化的策略实现
- 12.3 性能考虑
- 13. 策略模式的最佳实践
- 13.1 设计建议
- 13.2 代码示例:优化的策略实现
- 13.3 性能考虑
- 14. 总结
- 14.1 核心要点
- 14.2 常见应用场景
- 14.3 与Java 8+的结合
- 14.4 最终建议
1. 什么是策略模式?
策略模式是一种行为型设计模式,它定义了一系列算法,将每个算法封装起来,并使它们可以互相替换。策略模式让算法独立于使用它的客户端而变化,使得客户端可以在不修改代码的情况下,通过组合不同的策略对象来改变应用的行为。
策略模式的核心思想是:“定义一系列算法,封装每一个算法,并使它们可以互换。”
在策略模式中,我们创建表示各种策略的对象和一个行为随着策略对象改变而改变的上下文对象。策略对象改变上下文对象的执行算法。
2. 为什么需要策略模式?
在以下情况下,策略模式特别有用:
-
当需要在运行时选择不同的算法时:策略模式允许客户端在运行时选择不同的算法实现,而不需要修改或重新编译代码。
-
当有多种类似的处理方式或算法时:例如,不同的排序算法(冒泡排序、快速排序、归并排序等)或者不同的支付方式(信用卡支付、支付宝、微信支付等)。
-
当算法的使用涉及复杂的条件语句时:如果一个方法中包含了大量的条件判断(if-else或switch)来选择不同的算法,这会导致代码难以维护,策略模式可以消除这些条件判断。
-
当一个类定义了多种行为,并且这些行为在这个类的操作中以多个条件语句的形式出现时:策略模式可以将这些行为抽取到单独的策略类中,使主类更加简洁。
如果不使用策略模式,我们可能会遇到以下问题:
- 代码中充满了大量的条件语句
- 当增加新的策略时,需要修改客户端代码
- 算法和使用算法的客户端代码耦合度高
- 难以测试和维护
3. 策略模式的核心概念
策略模式主要包含以下几个核心概念:
3.1 策略(Strategy)
定义所有支持的算法的公共接口。上下文使用这个接口来调用具体策略定义的算法。
3.2 具体策略(Concrete Strategy)
实现策略接口的具体算法。
3.3 上下文(Context)
维护一个对策略对象的引用,并定义一个接口来让策略访问它的数据。上下文对象通常接受客户端的请求,然后委托给策略对象进行处理。
4. 策略模式的结构
策略模式通常包含以下角色:
- 策略(Strategy):定义所有支持的算法的公共接口
- 具体策略(Concrete Strategy):实现策略接口的具体算法
- 上下文(Context):维护一个对策略对象的引用,并定义一个接口来让策略访问它的数据
策略模式的 UML 类图如下:
+----------------+ +-------------------+
| Context |------->| Strategy |
+----------------+ +-------------------+
| -strategy | | +algorithmIfc() |
| +setStrategy() | +-------------------+
| +contextMethod()| |
+----------------+ ||+------------+-------------+| |+-----------------------+ +-----------------------+| ConcreteStrategyA | | ConcreteStrategyB |+-----------------------+ +-----------------------+| +algorithmIfc() | | +algorithmIfc() |+-----------------------+ +-----------------------+
5. 策略模式的基本实现
5.1 基础示例:不同的支付策略
假设我们正在开发一个在线购物系统,需要支持多种支付方式,如信用卡、支付宝和微信支付。我们可以使用策略模式来实现这个功能。
首先,定义一个策略接口:
// 支付策略接口
public interface PaymentStrategy {void pay(double amount);
}
然后,实现具体的策略类:
// 信用卡支付策略
public class CreditCardStrategy implements PaymentStrategy {private String name;private String cardNumber;private String cvv;private String dateOfExpiry;public CreditCardStrategy(String name, String cardNumber, String cvv, String dateOfExpiry) {this.name = name;this.cardNumber = cardNumber;this.cvv = cvv;this.dateOfExpiry = dateOfExpiry;}@Overridepublic void pay(double amount) {System.out.println(amount + " 元已通过信用卡支付");System.out.println("持卡人:" + name);System.out.println("卡号:" + cardNumber.substring(0, 4) + "********" + cardNumber.substring(cardNumber.length() - 4));}
}// 支付宝支付策略
public class AlipayStrategy implements PaymentStrategy {private String email;public AlipayStrategy(String email) {this.email = email;}@Overridepublic void pay(double amount) {System.out.println(amount + " 元已通过支付宝支付");System.out.println("支付宝账号:" + email);}
}// 微信支付策略
public class WeChatPayStrategy implements PaymentStrategy {private String id;public WeChatPayStrategy(String id) {this.id = id;}@Overridepublic void pay(double amount) {System.out.println(amount + " 元已通过微信支付");System.out.println("微信ID:" + id);}
}
接下来,创建上下文类:
// 购物车(上下文)
public class ShoppingCart {private List<Item> items;public ShoppingCart() {this.items = new ArrayList<>();}public void addItem(Item item) {items.add(item);}public void removeItem(Item item) {items.remove(item);}public double calculateTotal() {double sum = 0;for (Item item : items) {sum += item.getPrice();}return sum;}public void pay(PaymentStrategy paymentStrategy) {double amount = calculateTotal();paymentStrategy.pay(amount);}
}// 商品类
public class Item {private String name;private double price;public Item(String name, double price) {this.name = name;this.price = price;}public String getName() {return name;}public double getPrice() {return price;}
}
最后,客户端代码:
public class StrategyPatternDemo {public static void main(String[] args) {// 创建购物车并添加商品ShoppingCart cart = new ShoppingCart();cart.addItem(new Item("书籍", 50.0));cart.addItem(new Item("电影", 20.0));cart.addItem(new Item("音乐", 10.0));// 使用信用卡支付cart.pay(new CreditCardStrategy("张三", "1234567890123456", "123", "12/25"));System.out.println("\n");// 创建新的购物车并添加商品ShoppingCart cart2 = new ShoppingCart();cart2.addItem(new Item("手机", 1000.0));cart2.addItem(new Item("耳机", 100.0));// 使用支付宝支付cart2.pay(new AlipayStrategy("zhangsan@example.com"));System.out.println("\n");// 创建新的购物车并添加商品ShoppingCart cart3 = new ShoppingCart();cart3.addItem(new Item("笔记本电脑", 5000.0));// 使用微信支付cart3.pay(new WeChatPayStrategy("zhangsan123"));}
}
输出结果:
80.0 元已通过信用卡支付
持卡人:张三
卡号:1234********34561100.0 元已通过支付宝支付
支付宝账号:zhangsan@example.com5000.0 元已通过微信支付
微信ID:zhangsan123
在这个例子中:
- 策略接口:
PaymentStrategy
定义了所有支付策略的公共接口 - 具体策略:
CreditCardStrategy
、AlipayStrategy
和WeChatPayStrategy
实现了不同的支付算法 - 上下文:
ShoppingCart
维护了一个对策略对象的引用,并定义了一个接口来让策略访问它的数据
通过策略模式,我们可以在不修改上下文类的情况下,轻松地添加新的支付方式,例如银联支付、比特币支付等。
5.2 基础示例:不同的排序策略
让我们再看一个排序算法的例子,展示如何用策略模式封装不同的排序算法。
首先,定义一个排序策略接口:
// 排序策略接口
public interface SortStrategy {void sort(int[] array);
}
然后,实现具体的排序算法策略:
// 冒泡排序策略
public class BubbleSortStrategy implements SortStrategy {@Overridepublic void sort(int[] array) {System.out.println("使用冒泡排序算法...");int n = array.length;for (int i = 0; i < n - 1; i++) {for (int j = 0; j < n - i - 1; j++) {if (array[j] > array[j + 1]) {// 交换 array[j] 和 array[j+1]int temp = array[j];array[j] = array[j + 1];array[j + 1] = temp;}}}}
}// 插入排序策略
public class InsertionSortStrategy implements SortStrategy {@Overridepublic void sort(int[] array) {System.out.println("使用插入排序算法...");int n = array.length;for (int i = 1; i < n; i++) {int key = array[i];int j = i - 1;// 将比key大的元素向右移动while (j >= 0 && array[j] > key) {array[j + 1] = array[j];j = j - 1;}array[j + 1] = key;}}
}// 快速排序策略
public class QuickSortStrategy implements SortStrategy {@Overridepublic void sort(int[] array) {System.out.println("使用快速排序算法...");quickSort(array, 0, array.length - 1);}private void quickSort(int[] array, int low, int high) {if (low < high) {int pi = partition(array, low, high);quickSort(array, low, pi - 1);quickSort(array, pi + 1, high);}}private int partition(int[] array, int low, int high) {int pivot = array[high];int i = low - 1;for (int j = low; j < high; j++) {if (array[j] <= pivot) {i++;// 交换 array[i] 和 array[j]int temp = array[i];array[i] = array[j];array[j] = temp;}}// 交换 array[i+1] 和 array[high](pivot)int temp = array[i + 1];array[i + 1] = array[high];array[high] = temp;return i + 1;}
}
接下来,创建上下文类:
// 排序上下文
public class SortContext {private SortStrategy strategy;public SortContext(SortStrategy strategy) {this.strategy = strategy;}public void setStrategy(SortStrategy strategy) {this.strategy = strategy;}public void sortArray(int[] array) {strategy.sort(array);}public void printArray(int[] array) {for (int i : array) {System.out.print(i + " ");}System.out.println();}
}
最后,客户端代码:
public class SortStrategyDemo {public static void main(String[] args) {// 创建待排序数组int[] array1 = {64, 34, 25, 12, 22, 11, 90};int[] array2 = {64, 34, 25, 12, 22, 11, 90};int[] array3 = {64, 34, 25, 12, 22, 11, 90};// 创建上下文并设置不同的排序策略SortContext context = new SortContext(new BubbleSortStrategy());System.out.println("原始数组:");context.printArray(array1);// 使用冒泡排序context.sortArray(array1);System.out.println("冒泡排序后:");context.printArray(array1);// 切换为插入排序context.setStrategy(new InsertionSortStrategy());context.sortArray(array2);System.out.println("插入排序后:");context.printArray(array2);// 切换为快速排序context.setStrategy(new QuickSortStrategy());context.sortArray(array3);System.out.println("快速排序后:");context.printArray(array3);}
}
输出结果:
原始数组:
64 34 25 12 22 11 90
使用冒泡排序算法...
冒泡排序后:
11 12 22 25 34 64 90
使用插入排序算法...
插入排序后:
11 12 22 25 34 64 90
使用快速排序算法...
快速排序后:
11 12 22 25 34 64 90
在这个例子中:
- 策略接口:
SortStrategy
定义了所有排序策略的公共接口 - 具体策略:
BubbleSortStrategy
、InsertionSortStrategy
和QuickSortStrategy
实现了不同的排序算法 - 上下文:
SortContext
维护了一个对策略对象的引用,并定义了一个接口来让策略访问它的数据
通过策略模式,我们可以根据需要轻松地切换不同的排序算法,而不需要修改客户端代码的结构。此外,我们还可以轻松地添加新的排序算法,如归并排序、堆排序等。
6. 策略模式的高级实现
6.1 使用枚举实现策略模式
Java的枚举类型可以实现接口,这使得我们可以用枚举来实现策略模式,使代码更加简洁和类型安全。
// 使用枚举实现计算器策略
public enum CalculatorStrategy {ADD {@Overridepublic double calculate(double a, double b) {return a + b;}},SUBTRACT {@Overridepublic double calculate(double a, double b) {return a - b;}},MULTIPLY {@Overridepublic double calculate(double a, double b) {return a * b;}},DIVIDE {@Overridepublic double calculate(double a, double b) {if (b == 0) {throw new ArithmeticException("除数不能为零");}return a / b;}};public abstract double calculate(double a, double b);
}// 计算器上下文
public class Calculator {public double execute(double a, double b, CalculatorStrategy strategy) {return strategy.calculate(a, b);}
}
客户端代码:
public class EnumStrategyDemo {public static void main(String[] args) {Calculator calculator = new Calculator();double a = 10;double b = 5;System.out.println("加法: " + calculator.execute(a, b, CalculatorStrategy.ADD));System.out.println("减法: " + calculator.execute(a, b, CalculatorStrategy.SUBTRACT));System.out.println("乘法: " + calculator.execute(a, b, CalculatorStrategy.MULTIPLY));System.out.println("除法: " + calculator.execute(a, b, CalculatorStrategy.DIVIDE));try {System.out.println("除以零: " + calculator.execute(a, 0, CalculatorStrategy.DIVIDE));} catch (ArithmeticException e) {System.out.println("错误: " + e.getMessage());}}
}
输出结果:
加法: 15.0
减法: 5.0
乘法: 50.0
除法: 2.0
错误: 除数不能为零
使用枚举实现策略模式有以下优点:
- 类型安全:编译时检查,避免使用错误的策略
- 代码简洁:不需要创建多个策略类
- 单例保证:枚举实例在JVM中保证是单例的
6.2 使用Lambda表达式实现策略模式
在Java 8及以上版本中,我们可以使用Lambda表达式和函数式接口来简化策略模式的实现。
import java.util.function.BiFunction;// 使用函数式接口实现策略
public class FunctionalStrategyDemo {// 定义策略函数式接口@FunctionalInterfaceinterface ValidationStrategy {boolean validate(String text);}// 验证上下文static class Validator {private final ValidationStrategy strategy;public Validator(ValidationStrategy strategy) {this.strategy = strategy;}public boolean validate(String text) {return strategy.validate(text);}}public static void main(String[] args) {// 1. 使用Lambda表达式创建具体策略ValidationStrategy isAllLowerCase = s -> s.matches("^[a-z]+$");ValidationStrategy isNumeric = s -> s.matches("^\\d+$");// 2. 创建上下文对象Validator lowerCaseValidator = new Validator(isAllLowerCase);Validator numericValidator = new Validator(isNumeric);// 3. 执行验证String text1 = "hello";String text2 = "Hello";String text3 = "12345";System.out.println("'" + text1 + "' 全小写? " + lowerCaseValidator.validate(text1));System.out.println("'" + text2 + "' 全小写? " + lowerCaseValidator.validate(text2));System.out.println("'" + text1 + "' 全数字? " + numericValidator.validate(text1));System.out.println("'" + text3 + "' 全数字? " + numericValidator.validate(text3));// 4. 动态创建和使用策略System.out.println("\n使用即时创建的策略:");validateString(text1, s -> s.length() > 3, "长度大于3");validateString(text2, s -> s.startsWith("H"), "以H开头");validateString(text3, s -> Integer.parseInt(s) > 10000, "数值大于10000");// 5. 使用标准函数式接口System.out.println("\n使用BiFunction作为策略:");BiFunction<Double, Double, Double> addition = (a, b) -> a + b;BiFunction<Double, Double, Double> multiplication = (a, b) -> a * b;System.out.println("10 + 5 = " + executeOperation(10.0, 5.0, addition));System.out.println("10 * 5 = " + executeOperation(10.0, 5.0, multiplication));}private static void validateString(String text, ValidationStrategy strategy, String description) {System.out.println("'" + text + "' " + description + "? " + strategy.validate(text));}private static Double executeOperation(Double a, Double b, BiFunction<Double, Double, Double> strategy) {return strategy.apply(a, b);}
}
输出结果:
'hello' 全小写? true
'Hello' 全小写? false
'hello' 全数字? false
'12345' 全数字? true使用即时创建的策略:
'hello' 长度大于3? true
'Hello' 以H开头? true
'12345' 数值大于10000? false使用BiFunction作为策略:
10 + 5 = 15.0
10 * 5 = 50.0
使用Lambda表达式实现策略模式有以下优点:
- 代码更加简洁,减少了定义具体策略类的需要
- 可以在使用时即时创建策略,提高了灵活性
- 可以利用Java 8提供的标准函数式接口,如
Function
、BiFunction
、Predicate
等
6.3 使用策略工厂
当策略数量较多或者策略创建过程复杂时,我们可以使用工厂模式来管理策略的创建和选择。
import java.util.HashMap;
import java.util.Map;// 折扣策略接口
interface DiscountStrategy {double applyDiscount(double amount);
}// 会员折扣
class MemberDiscount implements DiscountStrategy {@Overridepublic double applyDiscount(double amount) {return amount * 0.9; // 10%折扣}
}// VIP折扣
class VIPDiscount implements DiscountStrategy {@Overridepublic double applyDiscount(double amount) {return amount * 0.8; // 20%折扣}
}// 新客户折扣
class NewCustomerDiscount implements DiscountStrategy {@Overridepublic double applyDiscount(double amount) {return amount * 0.95; // 5%折扣}
}// 节日折扣
class FestivalDiscount implements DiscountStrategy {@Overridepublic double applyDiscount(double amount) {return amount * 0.8; // 20%折扣}
}// 没有折扣
class NoDiscount implements DiscountStrategy {@Overridepublic double applyDiscount(double amount) {return amount; // 无折扣}
}// 折扣策略工厂
class DiscountStrategyFactory {private static Map<String, DiscountStrategy> strategies = new HashMap<>();static {strategies.put("MEMBER", new MemberDiscount());strategies.put("VIP", new VIPDiscount());strategies.put("NEW", new NewCustomerDiscount());strategies.put("FESTIVAL", new FestivalDiscount());strategies.put("NONE", new NoDiscount());}public static DiscountStrategy getStrategy(String type) {if (type == null || !strategies.containsKey(type)) {throw new IllegalArgumentException("无效的折扣类型");}return strategies.get(type);}// 允许注册新的策略public static void registerStrategy(String type, DiscountStrategy strategy) {strategies.put(type, strategy);}
}// 价格计算器上下文
class PriceCalculator {private DiscountStrategy strategy;public PriceCalculator(DiscountStrategy strategy) {this.strategy = strategy;}public void setStrategy(DiscountStrategy strategy) {this.strategy = strategy;}public double calculateFinalPrice(double price) {return strategy.applyDiscount(price);}
}
客户端代码:
public class StrategyFactoryDemo {public static void main(String[] args) {// 原始价格double originalPrice = 100.0;try {// 使用工厂获取不同的折扣策略DiscountStrategy memberStrategy = DiscountStrategyFactory.getStrategy("MEMBER");DiscountStrategy vipStrategy = DiscountStrategyFactory.getStrategy("VIP");DiscountStrategy newCustomerStrategy = DiscountStrategyFactory.getStrategy("NEW");DiscountStrategy festivalStrategy = DiscountStrategyFactory.getStrategy("FESTIVAL");DiscountStrategy noDiscountStrategy = DiscountStrategyFactory.getStrategy("NONE");// 创建价格计算器PriceCalculator calculator = new PriceCalculator(memberStrategy);// 计算会员价格System.out.println("会员价格: " + calculator.calculateFinalPrice(originalPrice));// 切换为VIP策略calculator.setStrategy(vipStrategy);System.out.println("VIP价格: " + calculator.calculateFinalPrice(originalPrice));// 切换为新客户策略calculator.setStrategy(newCustomerStrategy);System.out.println("新客户价格: " + calculator.calculateFinalPrice(originalPrice));// 切换为节日策略calculator.setStrategy(festivalStrategy);System.out.println("节日价格: " + calculator.calculateFinalPrice(originalPrice));// 切换为无折扣策略calculator.setStrategy(noDiscountStrategy);System.out.println("原价: " + calculator.calculateFinalPrice(originalPrice));// 注册一个新的折扣策略DiscountStrategyFactory.registerStrategy("SUPER_VIP", amount -> amount * 0.7); // 30%折扣// 使用新注册的策略calculator.setStrategy(DiscountStrategyFactory.getStrategy("SUPER_VIP"));System.out.println("超级VIP价格: " + calculator.calculateFinalPrice(originalPrice));// 尝试使用不存在的策略calculator.setStrategy(DiscountStrategyFactory.getStrategy("INVALID"));} catch (IllegalArgumentException e) {System.out.println("错误: " + e.getMessage());}}
}
输出结果:
会员价格: 90.0
VIP价格: 80.0
新客户价格: 95.0
节日价格: 80.0
原价: 100.0
超级VIP价格: 70.0
错误: 无效的折扣类型
使用策略工厂有以下优点:
- 集中管理所有的策略对象
- 封装策略的创建逻辑
- 可以动态地注册和获取策略
- 避免在客户端代码中直接创建策略对象,降低耦合度
7. 策略模式在Java中的实际应用
策略模式在Java标准库和流行框架中有广泛的应用。
7.1 Java Collections Framework中的排序
java.util.Collections.sort()
和java.util.Arrays.sort()
方法使用策略模式来支持自定义排序:
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;// 学生类
class Student {private String name;private int age;private double score;public Student(String name, int age, double score) {this.name = name;this.age = age;this.score = score;}public String getName() {return name;}public int getAge() {return age;}public double getScore() {return score;}@Overridepublic String toString() {return "Student{name='" + name + "', age=" + age + ", score=" + score + "}";}
}public class JavaSortStrategyDemo {public static void main(String[] args) {// 创建学生列表List<Student> students = new ArrayList<>();students.add(new Student("张三", 20, 85.5));students.add(new Student("李四", 22, 90.0));students.add(new Student("王五", 19, 78.5));students.add(new Student("赵六", 21, 92.5));students.add(new Student("钱七", 20, 88.0));System.out.println("原始学生列表:");printStudents(students);// 按年龄排序(使用匿名内部类)Collections.sort(students, new Comparator<Student>() {@Overridepublic int compare(Student s1, Student s2) {return Integer.compare(s1.getAge(), s2.getAge());}});System.out.println("\n按年龄排序后:");printStudents(students);// 按分数排序(使用Lambda表达式)Collections.sort(students, (s1, s2) -> Double.compare(s2.getScore(), s1.getScore()));System.out.println("\n按分数降序排序后:");printStudents(students);// 按姓名排序(使用方法引用)Collections.sort(students, Comparator.comparing(Student::getName));System.out.println("\n按姓名排序后:");printStudents(students);// 组合排序策略:先按年龄,年龄相同再按分数降序Comparator<Student> ageComparator = Comparator.comparing(Student::getAge);Comparator<Student> scoreComparator = Comparator.comparing(Student::getScore).reversed();Comparator<Student> combinedComparator = ageComparator.thenComparing(scoreComparator);Collections.sort(students, combinedComparator);System.out.println("\n先按年龄,再按分数降序排序后:");printStudents(students);}private static void printStudents(List<Student> students) {for (Student student : students) {System.out.println(student);}}
}
输出结果:
原始学生列表:
Student{name='张三', age=20, score=85.5}
Student{name='李四', age=22, score=90.0}
Student{name='王五', age=19, score=78.5}
Student{name='赵六', age=21, score=92.5}
Student{name='钱七', age=20, score=88.0}按年龄排序后:
Student{name='王五', age=19, score=78.5}
Student{name='张三', age=20, score=85.5}
Student{name='钱七', age=20, score=88.0}
Student{name='赵六', age=21, score=92.5}
Student{name='李四', age=22, score=90.0}按分数降序排序后:
Student{name='赵六', age=21, score=92.5}
Student{name='李四', age=22, score=90.0}
Student{name='钱七', age=20, score=88.0}
Student{name='张三', age=20, score=85.5}
Student{name='王五', age=19, score=78.5}按姓名排序后:
Student{name='李四', age=22, score=90.0}
Student{name='王五', age=19, score=78.5}
Student{name='张三', age=20, score=85.5}
Student{name='赵六', age=21, score=92.5}
Student{name='钱七', age=20, score=88.0}先按年龄,再按分数降序排序后:
Student{name='王五', age=19, score=78.5}
Student{name='钱七', age=20, score=88.0}
Student{name='张三', age=20, score=85.5}
Student{name='赵六', age=21, score=92.5}
Student{name='李四', age=22, score=90.0}
在这个例子中,Comparator<T>
接口充当了策略接口,不同的比较器实现了不同的排序策略。Collections.sort()
方法则是上下文,它接受一个列表和一个比较器,并根据比较器定义的策略对列表进行排序。
7.2 线程池中的拒绝策略
Java的ThreadPoolExecutor
类使用策略模式来处理线程池饱和时的拒绝策略:
import java.util.concurrent.*;
import java.util.concurrent.ThreadPoolExecutor.AbortPolicy;
import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;
import java.util.concurrent.ThreadPoolExecutor.DiscardPolicy;public class ThreadPoolRejectionStrategyDemo {public static void main(String[] args) {// 测试不同的拒绝策略testRejectionStrategy(new AbortPolicy());testRejectionStrategy(new DiscardPolicy());testRejectionStrategy(new DiscardOldestPolicy());testRejectionStrategy(new CallerRunsPolicy());// 自定义拒绝策略RejectedExecutionHandler customPolicy = (r, executor) -> {System.out.println("自定义拒绝策略: 任务 " + r.toString() + " 被拒绝");// 可以记录日志,发送通知,或者执行其他操作};testRejectionStrategy(customPolicy);}private static void testRejectionStrategy(RejectedExecutionHandler rejectionStrategy) {System.out.println("\n测试拒绝策略: " + rejectionStrategy.getClass().getSimpleName());// 创建一个容量极小的线程池ThreadPoolExecutor executor = new ThreadPoolExecutor(1, // 核心线程数2, // 最大线程数1, // 空闲线程存活时间TimeUnit.SECONDS,new ArrayBlockingQueue<>(2), // 有界队列,容量为2Executors.defaultThreadFactory(),rejectionStrategy // 使用指定的拒绝策略);try {// 提交5个任务,超过线程池处理能力,会触发拒绝策略for (int i = 1; i <= 5; i++) {final int taskId = i;executor.submit(() -> {try {System.out.println("执行任务 " + taskId + " 在线程: " + Thread.currentThread().getName());Thread.sleep(1000); // 模拟任务执行时间} catch (InterruptedException e) {Thread.currentThread().interrupt();}});System.out.println("提交任务 " + taskId);}} catch (Exception e) {System.out.println("异常: " + e.getMessage());} finally {executor.shutdown();try {executor.awaitTermination(5, TimeUnit.SECONDS);} catch (InterruptedException e) {Thread.currentThread().interrupt();}}}
}
输出结果(因为涉及到多线程,每次运行输出可能会有所不同):
测试拒绝策略: AbortPolicy
提交任务 1
提交任务 2
提交任务 3
执行任务 1 在线程: pool-1-thread-1
提交任务 4
异常: Task java.util.concurrent.FutureTask@15db9742 rejected from java.util.concurrent.ThreadPoolExecutor@6d06d69c[Running, pool size = 2, active threads = 2, queued tasks = 2, completed tasks = 0]测试拒绝策略: DiscardPolicy
提交任务 1
提交任务 2
提交任务 3
执行任务 1 在线程: pool-2-thread-1
提交任务 4
提交任务 5
执行任务 2 在线程: pool-2-thread-2
执行任务 3 在线程: pool-2-thread-1
执行任务 4 在线程: pool-2-thread-2测试拒绝策略: DiscardOldestPolicy
提交任务 1
提交任务 2
提交任务 3
执行任务 1 在线程: pool-3-thread-1
提交任务 4
提交任务 5
执行任务 3 在线程: pool-3-thread-2
执行任务 4 在线程: pool-3-thread-1
执行任务 5 在线程: pool-3-thread-2测试拒绝策略: CallerRunsPolicy
提交任务 1
提交任务 2
提交任务 3
执行任务 1 在线程: pool-4-thread-1
提交任务 4
执行任务 4 在线程: main
提交任务 5
执行任务 5 在线程: main
执行任务 2 在线程: pool-4-thread-2
执行任务 3 在线程: pool-4-thread-1测试拒绝策略: $1
提交任务 1
提交任务 2
提交任务 3
执行任务 1 在线程: pool-5-thread-1
提交任务 4
自定义拒绝策略: 任务 java.util.concurrent.FutureTask@4d7e1886 被拒绝
提交任务 5
自定义拒绝策略: 任务 java.util.concurrent.FutureTask@3cd1a2f1 被拒绝
执行任务 2 在线程: pool-5-thread-2
执行任务 3 在线程: pool-5-thread-1
在Java的线程池实现中,RejectedExecutionHandler
接口充当策略接口,有四种标准实现:
AbortPolicy
:直接抛出异常DiscardPolicy
:直接丢弃任务,不做任何处理DiscardOldestPolicy
:丢弃队列最前面的任务,然后重新提交被拒绝的任务CallerRunsPolicy
:由调用线程处理该任务
这是策略模式的一个很好的实际应用例子,允许开发者根据需要选择不同的拒绝策略,甚至实现自己的策略。
7.3 Spring框架中的策略模式
Spring框架广泛使用策略模式,例如在资源加载、事务管理和Bean实例化等方面。以下是一个简单的Spring Boot示例,展示如何在Spring中应用策略模式:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;import java.util.HashMap;
import java.util.Map;// 策略接口
interface NotificationStrategy {void sendNotification(String message);
}// 电子邮件通知策略
@Component("email")
class EmailNotification implements NotificationStrategy {@Overridepublic void sendNotification(String message) {System.out.println("通过电子邮件发送通知: " + message);}
}// 短信通知策略
@Component("sms")
class SMSNotification implements NotificationStrategy {@Overridepublic void sendNotification(String message) {System.out.println("通过短信发送通知: " + message);}
}// 微信通知策略
@Component("wechat")
class WeChatNotification implements NotificationStrategy {@Overridepublic void sendNotification(String message) {System.out.println("通过微信发送通知: " + message);}
}// 策略管理器
@Service
class NotificationStrategyManager {private final Map<String, NotificationStrategy> strategies;// 自动注入所有的策略实现public NotificationStrategyManager(Map<String, NotificationStrategy> strategies) {this.strategies = strategies;}public NotificationStrategy getStrategy(String strategyName) {return strategies.get(strategyName);}
}// 通知服务(上下文)
@Service
class NotificationService {private final NotificationStrategyManager strategyManager;public NotificationService(NotificationStrategyManager strategyManager) {this.strategyManager = strategyManager;}public void sendNotification(String message, String strategyName) {NotificationStrategy strategy = strategyManager.getStrategy(strategyName);if (strategy == null) {throw new IllegalArgumentException("未知的通知策略: " + strategyName);}strategy.sendNotification(message);}
}@SpringBootApplication
public class SpringStrategyPatternDemo {public static void main(String[] args) {var context = SpringApplication.run(SpringStrategyPatternDemo.class, args);// 获取通知服务NotificationService service = context.getBean(NotificationService.class);// 使用不同的通知策略service.sendNotification("重要消息!", "email");service.sendNotification("紧急通知!", "sms");service.sendNotification("会议提醒!", "wechat");// 关闭应用SpringApplication.exit(context);}
}
输出结果:
通过电子邮件发送通知: 重要消息!
通过短信发送通知: 紧急通知!
通过微信发送通知: 会议提醒!
在这个Spring Boot示例中:
NotificationStrategy
接口定义了通知策略- 具体策略类(
EmailNotification
、SMSNotification
、WeChatNotification
)使用@Component
注解,并指定bean名称 NotificationStrategyManager
负责管理和获取不同的策略NotificationService
是上下文类,根据指定的策略名称选择并执行相应的策略
Spring框架使这种实现变得简单,通过依赖注入自动管理策略对象,并提供了便捷的方法来获取和使用它们。
8. 策略模式与其他设计模式的比较
8.1 策略模式 vs 状态模式
相似点:
- 两者都封装行为到不同的类中
- 两者都通过组合和委托来改变对象的行为
- 都有上下文类来管理具体行为
不同点:
-
意图不同:
- 策略模式:封装可互换的行为,并使用委托来决定使用哪一个
- 状态模式:允许对象在内部状态改变时改变它的行为
-
行为改变的触发方式:
- 策略模式:由客户端代码显式地选择策略
- 状态模式:由上下文对象的内部状态自动地决定使用哪个状态对象
-
状态转换:
- 策略模式:通常没有策略对象之间的转换,策略的选择由客户端决定
- 状态模式:状态对象知道如何切换到另一个状态
下面是一个简单的例子展示两者的区别:
// 策略模式示例
interface MoveStrategy {void move();
}class WalkStrategy implements MoveStrategy {@Overridepublic void move() {System.out.println("Walking...");}
}class RunStrategy implements MoveStrategy {@Overridepublic void move() {System.out.println("Running...");}
}class Character {private MoveStrategy moveStrategy;public void setMoveStrategy(MoveStrategy moveStrategy) {this.moveStrategy = moveStrategy;}public void move() {if (moveStrategy != null) {moveStrategy.move();}}
}// 状态模式示例
interface MoveState {void move(CharacterWithState character);
}class WalkState implements MoveState {@Overridepublic void move(CharacterWithState character) {System.out.println("Walking...");// 可以改变状态,例如检测到快速移动输入if (Math.random() > 0.7) {System.out.println("Switching to running...");character.setState(new RunState());}}
}class RunState implements MoveState {@Overridepublic void move(CharacterWithState character) {System.out.println("Running...");// 可以改变状态,例如检测到持续时间过长if (Math.random() > 0.3) {System.out.println("Getting tired, switching to walking...");character.setState(new WalkState());}}
}class CharacterWithState {private MoveState state;public CharacterWithState() {this.state = new WalkState(); // 初始状态}public void setState(MoveState state) {this.state = state;}public void move() {state.move(this); // 状态可以改变自身}
}// 演示代码
public class StrategyVsStateDemo {public static void main(String[] args) {System.out.println("策略模式示例:");Character character = new Character();// 客户端选择策略character.setMoveStrategy(new WalkStrategy());character.move();character.setMoveStrategy(new RunStrategy());character.move();System.out.println("\n状态模式示例:");CharacterWithState characterWithState = new CharacterWithState();// 状态自动转换for (int i = 0; i < 5; i++) {characterWithState.move();}}
}
输出结果(因为状态转换有随机因素,结果可能会有所不同):
策略模式示例:
Walking...
Running...状态模式示例:
Walking...
Switching to running...
Running...
Getting tired, switching to walking...
Walking...
Walking...
Switching to running...
Running...
8.2 策略模式 vs 命令模式
相似点:
- 两者都封装行为到对象中
- 两者都使用组合而非继承来增强灵活性
不同点:
-
意图不同:
- 策略模式:定义一系列可互换的算法,使算法可以独立于使用它的客户而变化
- 命令模式:将请求封装为一个对象,从而使你可以用不同的请求对客户进行参数化
-
使用场景:
- 策略模式:当你需要在运行时选择不同的算法
- 命令模式:当你需要将操作封装为对象,支持撤销、排队、日志等功能
-
接收者:
- 策略模式:策略直接执行算法
- 命令模式:命令通常操作一个接收者对象,接收者执行实际工作
// 策略模式示例
interface TextFormattingStrategy {String format(String text);
}class UpperCaseStrategy implements TextFormattingStrategy {@Overridepublic String format(String text) {return text.toUpperCase();}
}class LowerCaseStrategy implements TextFormattingStrategy {@Overridepublic String format(String text) {return text.toLowerCase();}
}class Formatter {private TextFormattingStrategy strategy;public void setStrategy(TextFormattingStrategy strategy) {this.strategy = strategy;}public String format(String text) {if (strategy == null) {return text;}return strategy.format(text);}
}// 命令模式示例
interface TextCommand {String execute();void undo();
}class TextEditor {private String text;public TextEditor(String text) {this.text = text;}public void setText(String text) {this.text = text;}public String getText() {return text;}public String toUpperCase() {return text.toUpperCase();}public String toLowerCase() {return text.toLowerCase();}
}class UpperCaseCommand implements TextCommand {private TextEditor editor;private String previousText;public UpperCaseCommand(TextEditor editor) {this.editor = editor;}@Overridepublic String execute() {previousText = editor.getText();String upperCaseText = editor.toUpperCase();editor.setText(upperCaseText);return upperCaseText;}@Overridepublic void undo() {editor.setText(previousText);}
}class LowerCaseCommand implements TextCommand {private TextEditor editor;private String previousText;public LowerCaseCommand(TextEditor editor) {this.editor = editor;}@Overridepublic String execute() {previousText = editor.getText();String lowerCaseText = editor.toLowerCase();editor.setText(lowerCaseText);return lowerCaseText;}@Overridepublic void undo() {editor.setText(previousText);}
}// 命令调用者
class CommandInvoker {private List<TextCommand> history = new ArrayList<>();public String executeCommand(TextCommand command) {String result = command.execute();history.add(command);return result;}public void undoLastCommand() {if (!history.isEmpty()) {TextCommand command = history.remove(history.size() - 1);command.undo();}}
}// 演示代码
public class StrategyVsCommandDemo {public static void main(String[] args) {String text = "Hello Strategy and Command Patterns";System.out.println("原始文本: " + text);// 策略模式示例System.out.println("\n策略模式示例:");Formatter formatter = new Formatter();formatter.setStrategy(new UpperCaseStrategy());System.out.println("大写策略: " + formatter.format(text));formatter.setStrategy(new LowerCaseStrategy());System.out.println("小写策略: " + formatter.format(text));// 命令模式示例System.out.println("\n命令模式示例:");TextEditor editor = new TextEditor(text);CommandInvoker invoker = new CommandInvoker();System.out.println("执行大写命令: " + invoker.executeCommand(new UpperCaseCommand(editor)));System.out.println("执行小写命令: " + invoker.executeCommand(new LowerCaseCommand(editor)));invoker.undoLastCommand();System.out.println("撤销后: " + editor.getText());invoker.undoLastCommand();System.out.println("再次撤销后: " + editor.getText());}
}
输出结果:
原始文本: Hello Strategy and Command Patterns策略模式示例:
大写策略: HELLO STRATEGY AND COMMAND PATTERNS
小写策略: hello strategy and command patterns命令模式示例:
执行大写命令: HELLO STRATEGY AND COMMAND PATTERNS
执行小写命令: hello strategy and command patterns
撤销后: HELLO STRATEGY AND COMMAND PATTERNS
再次撤销后: Hello Strategy and Command Patterns
8.3 策略模式 vs 工厂模式
相似点:
- 两者都创建对象
- 两者都使用多态性和接口
- 两者都能减少条件语句的使用
不同点:
-
意图不同:
- 策略模式:更改对象的行为
- 工厂模式:创建对象而不指定创建哪个具体类
-
使用时机:
- 策略模式:当你有多种算法可供选择,并希望在运行时选择其中之一
- 工厂模式:当你需要创建一个对象,但事先不知道应该创建哪个具体类的对象
-
关注点:
- 策略模式:关注行为的多样性和可替换性
- 工厂模式:关注对象的创建过程
// 策略模式示例
interface SortStrategy {void sort(int[] array);
}class QuickSortStrategy implements SortStrategy {@Overridepublic void sort(int[] array) {System.out.println("使用快速排序...");// 快速排序实现...}
}class MergeSortStrategy implements SortStrategy {@Overridepublic void sort(int[] array) {System.out.println("使用归并排序...");// 归并排序实现...}
}class ArraySorter {private SortStrategy strategy;public void setSortStrategy(SortStrategy strategy) {this.strategy = strategy;}public void sort(int[] array) {strategy.sort(array);}
}// 工厂模式示例
interface Logger {void log(String message);
}class FileLogger implements Logger {@Overridepublic void log(String message) {System.out.println("文件日志: " + message);}
}class ConsoleLogger implements Logger {@Overridepublic void log(String message) {System.out.println("控制台日志: " + message);}
}class DatabaseLogger implements Logger {@Overridepublic void log(String message) {System.out.println("数据库日志: " + message);}
}class LoggerFactory {public static Logger createLogger(String type) {switch (type.toLowerCase()) {case "file":return new FileLogger();case "console":return new ConsoleLogger();case "database":return new DatabaseLogger();default:throw new IllegalArgumentException("未知的日志类型: " + type);}}
}// 演示代码
public class StrategyVsFactoryDemo {public static void main(String[] args) {// 策略模式示例System.out.println("策略模式示例:");int[] array = {5, 2, 8, 1, 9};ArraySorter sorter = new ArraySorter();sorter.setSortStrategy(new QuickSortStrategy());sorter.sort(array);sorter.setSortStrategy(new MergeSortStrategy());sorter.sort(array);// 工厂模式示例System.out.println("\n工厂模式示例:");Logger fileLogger = LoggerFactory.createLogger("file");Logger consoleLogger = LoggerFactory.createLogger("console");Logger databaseLogger = LoggerFactory.createLogger("database");fileLogger.log("这是一条文件日志消息");consoleLogger.log("这是一条控制台日志消息");databaseLogger.log("这是一条数据库日志消息");}
}
输出结果:
策略模式示例:
使用快速排序...
使用归并排序...工厂模式示例:
文件日志: 这是一条文件日志消息
控制台日志: 这是一条控制台日志消息
数据库日志: 这是一条数据库日志消息
8.4 策略模式 vs 模板方法模式
相似点:
- 两者都定义算法的一部分,让子类完成其他部分
- 两者都用于处理算法变化
不同点:
-
实现方式:
- 策略模式:使用组合和委托
- 模板方法模式:使用继承和方法重写
-
扩展点:
- 策略模式:整个算法都是可替换的
- 模板方法模式:只有算法中的特定步骤是可替换的,而整体结构是固定的
-
控制反转:
- 策略模式:控制权在客户端
- 模板方法模式:控制权在父类
// 策略模式示例
interface CoffeeMakingStrategy {void makeCoffee();
}class AmericanoCoffeeStrategy implements CoffeeMakingStrategy {@Overridepublic void makeCoffee() {System.out.println("1. 研磨咖啡豆");System.out.println("2. 冲泡浓缩咖啡");System.out.println("3. 添加热水");System.out.println("4. 美式咖啡制作完成");}
}class LatteStrategy implements CoffeeMakingStrategy {@Overridepublic void makeCoffee() {System.out.println("1. 研磨咖啡豆");System.out.println("2. 冲泡浓缩咖啡");System.out.println("3. 蒸牛奶");System.out.println("4. 倒入牛奶");System.out.println("5. 拿铁制作完成");}
}class CoffeeMachine {private CoffeeMakingStrategy strategy;public void setStrategy(CoffeeMakingStrategy strategy) {this.strategy = strategy;}public void makeCoffee() {strategy.makeCoffee();}
}// 模板方法模式示例
abstract class CoffeeTemplate {// 模板方法public final void makeCoffee() {boilWater();grindCoffeeBeans();brewCoffee();addCondiments();System.out.println("咖啡制作完成");}// 这些是固定步骤private void boilWater() {System.out.println("将水煮沸");}private void grindCoffeeBeans() {System.out.println("研磨咖啡豆");}// 这些是可以由子类重写的步骤protected abstract void brewCoffee();protected abstract void addCondiments();
}class AmericanoCoffee extends CoffeeTemplate {@Overrideprotected void brewCoffee() {System.out.println("冲泡浓缩咖啡");}@Overrideprotected void addCondiments() {System.out.println("添加热水");}
}class LatteCoffee extends CoffeeTemplate {@Overrideprotected void brewCoffee() {System.out.println("冲泡浓缩咖啡");}@Overrideprotected void addCondiments() {System.out.println("蒸牛奶并倒入");}
}// 演示代码
public class StrategyVsTemplateMethodDemo {public static void main(String[] args) {// 策略模式示例System.out.println("策略模式示例:");CoffeeMachine machine = new CoffeeMachine();System.out.println("制作美式咖啡:");machine.setStrategy(new AmericanoCoffeeStrategy());machine.makeCoffee();System.out.println("\n制作拿铁:");machine.setStrategy(new LatteStrategy());machine.makeCoffee();// 模板方法模式示例System.out.println("\n模板方法模式示例:");System.out.println("制作美式咖啡:");CoffeeTemplate americano = new AmericanoCoffee();americano.makeCoffee();System.out.println("\n制作拿铁:");CoffeeTemplate latte = new LatteCoffee();latte.makeCoffee();}
}
输出结果:
策略模式示例:
制作美式咖啡:
1. 研磨咖啡豆
2. 冲泡浓缩咖啡
3. 添加热水
4. 美式咖啡制作完成制作拿铁:
1. 研磨咖啡豆
2. 冲泡浓缩咖啡
3. 蒸牛奶
4. 倒入牛奶
5. 拿铁制作完成模板方法模式示例:
制作美式咖啡:
将水煮沸
研磨咖啡豆
冲泡浓缩咖啡
添加热水
咖啡制作完成制作拿铁:
将水煮沸
研磨咖啡豆
冲泡浓缩咖啡
蒸牛奶并倒入
咖啡制作完成
9. 策略模式的优缺点
9.1 优点
-
开闭原则:你可以在不修改上下文代码的情况下引入新的策略,符合"对扩展开放,对修改封闭"的原则。
-
代码复用:通过策略模式,可以避免重复的条件语句,将算法封装在独立的策略类中,这些策略可以被多个上下文复用。
-
可维护性:每个策略类可以独立演化,当你修改一个策略类时,不会影响到其他策略类或上下文类。
-
运行时切换行为:策略可以在运行时动态替换,提供了更大的灵活性。
-
更好的测试性:每个策略都是独立的,可以单独测试,符合单一职责原则。
-
消除条件语句:避免了复杂的条件判断语句,如多层if-else或switch语句。
-
客户端代码简化:客户端只需要知道策略接口,而不需要了解具体策略的实现细节。
9.2 缺点
-
增加类的数量:每个策略对应一个类,会增加系统中类的数量,使系统更加复杂。
-
客户端必须了解所有策略:客户端需要知道所有的策略类,以便选择合适的策略,这增加了客户端和策略之间的耦合。
-
策略的增加会导致对象数量的增加:如果有很多策略,将会创建大量的对象,可能会影响性能。
-
数据共享的问题:不同的策略可能需要不同的数据,有时候很难以优雅的方式在上下文和不同策略之间共享数据。
-
上下文和策略的通信开销:策略和上下文之间可能需要通信,这可能带来额外的开销。
10. 何时使用策略模式?
在以下情况下,应考虑使用策略模式:
-
当你需要在运行时选择不同算法的变体时:例如,不同的排序算法、不同的验证规则、不同的价格计算方法等。
-
当你有多个类,它们的区别仅在于它们的行为时:你可以将不同的行为抽取到单独的策略类中,而不是创建多个子类。
-
当你的类中有大量的条件语句,用于选择不同的行为时:可以将这些条件语句的分支放入不同的策略类中,以减少条件语句的数量。
-
当你需要隐藏复杂算法的实现细节时:策略模式可以帮助你将复杂的算法实现细节封装起来,对客户端隐藏。
-
当算法使用的数据,客户端不应该知道时:策略模式可以将这些数据封装在策略类中,对客户端隐藏。
-
当有多种可能的行为,且在运行时需要从中选择一种时:例如,加密算法的选择、压缩算法的选择等。
10.1 实际应用场景
-
支付系统:信用卡支付、支付宝支付、微信支付等不同的支付方式。
-
数据验证:不同类型数据的验证策略,如电子邮件验证、电话号码验证、密码强度验证等。
-
文本排版:不同的文本排版策略,如左对齐、右对齐、居中对齐等。
-
图像压缩:不同的图像压缩算法,如JPEG、PNG、GIF等。
-
税费计算:不同国家或地区的税费计算方法。
-
路径规划:不同的路径规划算法,如最短路径、最快路径、最省油路径等。
-
游戏AI:不同难度级别的游戏AI策略,如简单、中等、困难。
10.2 Java中常见的策略模式应用
-
Java集合框架中的排序:
Collections.sort()
方法接受一个Comparator
对象,用于定义排序策略。 -
线程池的拒绝策略:
ThreadPoolExecutor
类的拒绝策略,如AbortPolicy
、CallerRunsPolicy
、DiscardPolicy
和DiscardOldestPolicy
。 -
Spring框架中的资源加载策略:
ResourceLoader
接口的不同实现,用于加载不同类型的资源。 -
Spring Security中的认证策略:不同的认证提供者,如数据库认证、LDAP认证、OAuth认证等。
-
Java NIO中的通道选择策略:
Selector
类使用不同的策略来选择就绪的通道。
11. 常见问题与解决方案
11.1 策略选择问题
问题:如何在运行时动态选择合适的策略?
解决方案:
- 使用工厂类或策略注册表来管理和创建策略
- 使用配置文件或数据库来存储策略选择规则
- 使用上下文信息(如用户偏好、系统状态等)来选择策略
// 策略注册表
class StrategyRegistry {private static Map<String, PaymentStrategy> strategies = new HashMap<>();static {strategies.put("CREDIT_CARD", new CreditCardStrategy("Default", "0000", "000", "01/30"));strategies.put("ALIPAY", new AlipayStrategy("default@example.com"));strategies.put("WECHAT", new WeChatPayStrategy("default_id"));}public static PaymentStrategy getStrategy(String type) {PaymentStrategy strategy = strategies.get(type);if (strategy == null) {throw new IllegalArgumentException("未知的支付类型: " + type);}return strategy;}public static void registerStrategy(String type, PaymentStrategy strategy) {strategies.put(type, strategy);}
}// 根据用户偏好选择策略
class UserPreferenceBasedStrategySelector {public static PaymentStrategy selectStrategy(User user) {// 检查用户首选支付方式String preferredPaymentMethod = user.getPreferredPaymentMethod();if (preferredPaymentMethod != null) {try {return StrategyRegistry.getStrategy(preferredPaymentMethod);} catch (IllegalArgumentException e) {// 如果首选方式无效,回退到默认方式System.out.println("用户首选支付方式无效,使用默认方式");}}// 根据用户历史行为选择if (user.hasUsedCreditCard()) {return StrategyRegistry.getStrategy("CREDIT_CARD");} else if (user.hasUsedAlipay()) {return StrategyRegistry.getStrategy("ALIPAY");} else {// 默认支付方式return StrategyRegistry.getStrategy("WECHAT");}}
}
11.2 参数传递问题
问题:当不同的策略需要不同的参数时,如何处理?
解决方案:
- 使用上下文对象传递共享数据
- 使用策略接口方法的参数传递特定数据
- 使用构建器或工厂方法创建预配置的策略
// 带上下文的策略接口
interface TaxCalculationStrategy {double calculateTax(TaxContext context);
}// 上下文类
class TaxContext {private double income;private String country;private boolean isResident;private int dependents;private Map<String, Object> additionalData = new HashMap<>();// 构造器、getter和setter方法public void setAdditionalData(String key, Object value) {additionalData.put(key, value);}public Object getAdditionalData(String key) {return additionalData.get(key);}
}// 中国税务策略
class ChinaTaxStrategy implements TaxCalculationStrategy {@Overridepublic double calculateTax(TaxContext context) {double income = context.getIncome();boolean isResident = context.isResident();int dependents = context.getDependents();// 计算中国税收...return income * 0.2 - (isResident ? dependents * 1000 : 0);}
}// 美国税务策略
class USTaxStrategy implements TaxCalculationStrategy {@Overridepublic double calculateTax(TaxContext context) {double income = context.getIncome();boolean isResident = context.isResident();int dependents = context.getDependents();// 获取特定于美国税收的额外数据String state = (String) context.getAdditionalData("state");boolean hasMedicalInsurance = (boolean) context.getAdditionalData("hasMedicalInsurance");// 计算美国税收...double federalTax = income * 0.15 - (isResident ? dependents * 2000 : 0);double stateTax = "California".equals(state) ? income * 0.08 : income * 0.05;double healthCareDeduction = hasMedicalInsurance ? 1500 : 0;return federalTax + stateTax - healthCareDeduction;}
}// 税务计算器
class TaxCalculator {private TaxCalculationStrategy strategy;public TaxCalculator(TaxCalculationStrategy strategy) {this.strategy = strategy;}public void setStrategy(TaxCalculationStrategy strategy) {this.strategy = strategy;}public double calculateTax(TaxContext context) {return strategy.calculateTax(context);}
}
客户端代码:
public class TaxCalculationDemo {public static void main(String[] args) {// 创建上下文TaxContext chinaContext = new TaxContext();chinaContext.setIncome(100000);chinaContext.setCountry("China");chinaContext.setResident(true);chinaContext.setDependents(2);TaxContext usContext = new TaxContext();usContext.setIncome(100000);usContext.setCountry("USA");usContext.setResident(true);usContext.setDependents(2);usContext.setAdditionalData("state", "California");usContext.setAdditionalData("hasMedicalInsurance", true);// 创建计算器并使用不同的策略TaxCalculator calculator = new TaxCalculator(new ChinaTaxStrategy());System.out.println("中国税收: " + calculator.calculateTax(chinaContext));calculator.setStrategy(new USTaxStrategy());System.out.println("美国税收: " + calculator.calculateTax(usContext));}
}
输出结果:
中国税收: 20000.0
美国税收: 15000.0
11.3 策略组合问题
问题:当需要组合多个策略时,如何处理?
解决方案:
- 使用组合模式来创建复合策略
- 使用装饰器模式来动态添加策略行为
- 使用责任链模式来按顺序应用多个策略
// 验证策略接口
interface ValidationStrategy {boolean validate(String input);String getErrorMessage();
}// 长度验证策略
class LengthValidationStrategy implements ValidationStrategy {private int minLength;private int maxLength;public LengthValidationStrategy(int minLength, int maxLength) {this.minLength = minLength;this.maxLength = maxLength;}@Overridepublic boolean validate(String input) {int length = input.length();return length >= minLength && length <= maxLength;}@Overridepublic String getErrorMessage() {return "长度必须在 " + minLength + " 到 " + maxLength + " 之间";}
}// 正则表达式验证策略
class RegexValidationStrategy implements ValidationStrategy {private String regex;private String errorMessage;public RegexValidationStrategy(String regex, String errorMessage) {this.regex = regex;this.errorMessage = errorMessage;}@Overridepublic boolean validate(String input) {return input.matches(regex);}@Overridepublic String getErrorMessage() {return errorMessage;}
}// 组合验证策略
class CompositeValidationStrategy implements ValidationStrategy {private List<ValidationStrategy> strategies = new ArrayList<>();private String failedStrategy = null;public void addStrategy(ValidationStrategy strategy) {strategies.add(strategy);}@Overridepublic boolean validate(String input) {for (ValidationStrategy strategy : strategies) {if (!strategy.validate(input)) {failedStrategy = strategy.getErrorMessage();return false;}}return true;}@Overridepublic String getErrorMessage() {return failedStrategy;}
}// 装饰器验证策略
class TrimValidationDecorator implements ValidationStrategy {private ValidationStrategy wrappedStrategy;public TrimValidationDecorator(ValidationStrategy wrappedStrategy) {this.wrappedStrategy = wrappedStrategy;}@Overridepublic boolean validate(String input) {// 去除前后空格后再验证return wrappedStrategy.validate(input.trim());}@Overridepublic String getErrorMessage() {return wrappedStrategy.getErrorMessage();}
}
客户端代码:
public class CompositeStrategyDemo {public static void main(String[] args) {// 创建单个策略ValidationStrategy lengthStrategy = new LengthValidationStrategy(8, 20);ValidationStrategy passwordRegexStrategy = new RegexValidationStrategy("^(?=.*[0-9])(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$","密码必须包含数字、小写字母、大写字母和特殊字符");ValidationStrategy emailRegexStrategy = new RegexValidationStrategy("^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$","电子邮件格式无效");// 创建组合策略CompositeValidationStrategy passwordValidationStrategy = new CompositeValidationStrategy();passwordValidationStrategy.addStrategy(lengthStrategy);passwordValidationStrategy.addStrategy(passwordRegexStrategy);// 创建装饰器策略ValidationStrategy trimmedEmailValidator = new TrimValidationDecorator(emailRegexStrategy);// 测试密码验证String password1 = "Abc123";String password2 = "Abc123@xyz";System.out.println("密码 '" + password1 + "' 验证结果: " + (passwordValidationStrategy.validate(password1) ? "有效" : "无效 - " + passwordValidationStrategy.getErrorMessage()));System.out.println("密码 '" + password2 + "' 验证结果: " + (passwordValidationStrategy.validate(password2) ? "有效" : "无效 - " + passwordValidationStrategy.getErrorMessage()));// 测试邮箱验证String email1 = " user@example.com ";String email2 = "invalid-email";System.out.println("邮箱 '" + email1 + "' 验证结果: " + (trimmedEmailValidator.validate(email1) ? "有效" : "无效 - " + trimmedEmailValidator.getErrorMessage()));System.out.println("邮箱 '" + email2 + "' 验证结果: " + (trimmedEmailValidator.validate(email2) ? "有效" : "无效 - " + trimmedEmailValidator.getErrorMessage()));}
}
输出结果:
密码 'Abc123' 验证结果: 有效
密码 'Abc123@xyz' 验证结果: 有效
邮箱 ' user@example.com ' 验证结果: 有效
邮箱 'invalid-email' 验证结果: 无效 - 电子邮件格式无效
12. 策略模式的最佳实践
12.1 设计建议
-
使用接口定义策略:策略应该通过接口定义,以便于替换和测试。避免使用抽象类,除非有共享代码。
-
保持策略接口简单:策略接口应该尽可能简单,通常只有一个方法,这样更容易实现和维护。
-
考虑默认策略:提供一个默认策略,以防客户端没有指定策略或提供了无效的策略。
-
使策略无状态:尽量设计无状态的策略,使其更加可重用。如果需要状态,确保它在策略方法的调用之间不会保留。
-
命名清晰:策略的名称应当清晰地表示其意图,例如
SortStrategy
、ValidationStrategy
等。 -
将策略组合化:如果有多个相关的策略决策,考虑使用组合模式来创建复合策略。
12.2 代码示例:优化的策略实现
下面是一个优化的策略模式实现,遵循最佳实践:
// 定义一个简单的功能接口(Java 8+)
@FunctionalInterface
interface FilterStrategy<T> {boolean test(T item);
}// 推荐项过滤器
class RecommendationFilter<T> {private FilterStrategy<T> strategy;// 1. 提供一个默认策略public RecommendationFilter() {this(item -> true); // 默认接受所有项}public RecommendationFilter(FilterStrategy<T> strategy) {this.strategy = strategy;}// 2. 允许动态更改策略public void setStrategy(FilterStrategy<T> strategy) {this.strategy = strategy;}// 3. 应用策略过滤集合public List<T> filter(List<T> items) {List<T> result = new ArrayList<>();for (T item : items) {if (strategy.test(item)) {result.add(item);}}return result;}// 4. 支持策略组合public static <T> FilterStrategy<T> and(FilterStrategy<T> strategy1, FilterStrategy<T> strategy2) {return item -> strategy1.test(item) && strategy2.test(item);}public static <T> FilterStrategy<T> or(FilterStrategy<T> strategy1, FilterStrategy<T> strategy2) {return item -> strategy1.test(item) || strategy2.test(item);}public static <T> FilterStrategy<T> not(FilterStrategy<T> strategy) {return item -> !strategy.test(item);}
}// 产品类
class Product {private String name;private double price;private double rating;private String category;public Product(String name, double price, double rating, String category) {this.name = name;this.price = price;this.rating = rating;this.category = category;}public String getName() { return name; }public double getPrice() { return price; }public double getRating() { return rating; }public String getCategory() { return category; }@Overridepublic String toString() {return String.format("%s (%.2f元, %.1f星, %s)", name, price, rating, category);}
}// 策略工具类
class ProductStrategies {// 5. 提供常用策略的静态工厂方法public static FilterStrategy<Product> priceBelow(double maxPrice) {return product -> product.getPrice() <= maxPrice;}public static FilterStrategy<Product> minRating(double minRating) {return product -> product.getRating() >= minRating;}public static FilterStrategy<Product> inCategory(String category) {return product -> product.getCategory().equals(category);}
}
客户端代码:
public class StrategyBestPracticesDemo {public static void main(String[] args) {// 创建产品列表List<Product> products = Arrays.asList(new Product("手机A", 3999, 4.5, "电子产品"),new Product("手机B", 2499, 4.0, "电子产品"),new Product("笔记本电脑", 6999, 4.8, "电子产品"),new Product("耳机", 899, 4.3, "电子产品"),new Product("书籍A", 59, 4.5, "图书"),new Product("书籍B", 39, 3.5, "图书"),new Product("运动鞋", 499, 4.2, "服装"),new Product("T恤", 99, 3.8, "服装"));System.out.println("所有产品:");products.forEach(System.out::println);// 创建推荐过滤器RecommendationFilter<Product> filter = new RecommendationFilter<>();// 使用Lambda表达式定义匿名策略System.out.println("\n价格低于1000元的产品:");filter.setStrategy(product -> product.getPrice() < 1000);printFilteredProducts(filter.filter(products));// 使用预定义策略System.out.println("\n电子产品类别中评分4.0以上的产品:");FilterStrategy<Product> electronicStrategy = ProductStrategies.inCategory("电子产品");FilterStrategy<Product> highRatingStrategy = ProductStrategies.minRating(4.0);// 组合策略filter.setStrategy(RecommendationFilter.and(electronicStrategy, highRatingStrategy));printFilteredProducts(filter.filter(products));// 价格低于1000元或评分高于4.5的产品System.out.println("\n价格低于1000元或评分高于4.5的产品:");FilterStrategy<Product> lowPriceStrategy = ProductStrategies.priceBelow(1000);FilterStrategy<Product> veryHighRatingStrategy = ProductStrategies.minRating(4.5);filter.setStrategy(RecommendationFilter.or(lowPriceStrategy, veryHighRatingStrategy));printFilteredProducts(filter.filter(products));// 非电子产品且评分低于4.0的产品System.out.println("\n非电子产品且评分低于4.0的产品:");FilterStrategy<Product> nonElectronicStrategy = RecommendationFilter.not(electronicStrategy);FilterStrategy<Product> lowRatingStrategy = RecommendationFilter.not(ProductStrategies.minRating(4.0));filter.setStrategy(RecommendationFilter.and(nonElectronicStrategy, lowRatingStrategy));printFilteredProducts(filter.filter(products));}private static void printFilteredProducts(List<Product> products) {if (products.isEmpty()) {System.out.println("没有找到符合条件的产品");} else {products.forEach(System.out::println);}}
}
输出结果:
所有产品:
手机A (3999.00元, 4.5星, 电子产品)
手机B (2499.00元, 4.0星, 电子产品)
笔记本电脑 (6999.00元, 4.8星, 电子产品)
耳机 (899.00元, 4.3星, 电子产品)
书籍A (59.00元, 4.5星, 图书)
书籍B (39.00元, 3.5星, 图书)
运动鞋 (499.00元, 4.2星, 服装)
T恤 (99.00元, 3.8星, 服装)价格低于1000元的产品:
耳机 (899.00元, 4.3星, 电子产品)
书籍A (59.00元, 4.5星, 图书)
书籍B (39.00元, 3.5星, 图书)
运动鞋 (499.00元, 4.2星, 服装)
T恤 (99.00元, 3.8星, 服装)电子产品类别中评分4.0以上的产品:
手机A (3999.00元, 4.5星, 电子产品)
手机B (2499.00元, 4.0星, 电子产品)
笔记本电脑 (6999.00元, 4.8星, 电子产品)
耳机 (899.00元, 4.3星, 电子产品)价格低于1000元或评分高于4.5的产品:
手机A (3999.00元, 4.5星, 电子产品)
笔记本电脑 (6999.00元, 4.8星, 电子产品)
耳机 (899.00元, 4.3星, 电子产品)
书籍A (59.00元, 4.5星, 图书)
书籍B (39.00元, 3.5星, 图书)
运动鞋 (499.00元, 4.2星, 服装)
T恤 (99.00元, 3.8星, 服装)非电子产品且评分低于4.0的产品:
书籍B (39.00元, 3.5星, 图书)
T恤 (99.00元, 3.8星, 服装)
这个示例展示了几个最佳实践:
- 使用函数式接口简化策略定义
- 提供默认策略
- 支持策略组合
- 提供静态工厂方法创建常用策略
- 使用Lambda表达式创建匿名策略
12.3 性能考虑
策略模式在提供灵活性的同时,也可能带来一些性能开销。以下是一些优化策略模式性能的方法:
-
策略复用:对于无状态的策略,可以将其实现为单例或静态工厂方法,避免重复创建相同的策略对象。
-
懒加载策略:只在真正需要时才创建策略对象。
-
缓存策略结果:如果策略计算开销大,可以考虑缓存结果。
-
适当的粒度:选择合适的策略粒度,太细粒度可能导致类爆炸,太粗粒度可能失去灵活性。
// 缓存策略的实现示例
class CachingPricingStrategy implements PricingStrategy {private final PricingStrategy delegate;private final Map<String, Double> cache = new HashMap<>();public CachingPricingStrategy(PricingStrategy delegate) {this.delegate = delegate;}@Overridepublic double calculatePrice(Product product) {String key = product.getId();if (cache.containsKey(key)) {System.out.println("从缓存获取价格: " + product.getName());return cache.get(key);}System.out.println("计算价格: " + product.getName());double price = delegate.calculatePrice(product);cache.put(key, price);return price;}public void clearCache() {cache.clear();}
}
13. 策略模式的最佳实践
13.1 设计建议
-
使用接口定义策略:策略应该通过接口定义,以便于替换和测试。避免使用抽象类,除非有共享代码。
-
保持策略接口简单:策略接口应该尽可能简单,通常只有一个方法,这样更容易实现和维护。
-
考虑默认策略:提供一个默认策略,以防客户端没有指定策略或提供了无效的策略。
-
使策略无状态:尽量设计无状态的策略,使其更加可重用。如果需要状态,确保它在策略方法的调用之间不会保留。
-
命名清晰:策略的名称应当清晰地表示其意图,例如
SortStrategy
、ValidationStrategy
等。 -
将策略组合化:如果有多个相关的策略决策,考虑使用组合模式来创建复合策略。
13.2 代码示例:优化的策略实现
下面是一个优化的策略模式实现,遵循最佳实践:
// 定义一个简单的功能接口(Java 8+)
@FunctionalInterface
interface FilterStrategy<T> {boolean test(T item);
}// 推荐项过滤器
class RecommendationFilter<T> {private FilterStrategy<T> strategy;// 1. 提供一个默认策略public RecommendationFilter() {this(item -> true); // 默认接受所有项}public RecommendationFilter(FilterStrategy<T> strategy) {this.strategy = strategy;}// 2. 允许动态更改策略public void setStrategy(FilterStrategy<T> strategy) {this.strategy = strategy;}// 3. 应用策略过滤集合public List<T> filter(List<T> items) {List<T> result = new ArrayList<>();for (T item : items) {if (strategy.test(item)) {result.add(item);}}return result;}// 4. 支持策略组合public static <T> FilterStrategy<T> and(FilterStrategy<T> strategy1, FilterStrategy<T> strategy2) {return item -> strategy1.test(item) && strategy2.test(item);}public static <T> FilterStrategy<T> or(FilterStrategy<T> strategy1, FilterStrategy<T> strategy2) {return item -> strategy1.test(item) || strategy2.test(item);}public static <T> FilterStrategy<T> not(FilterStrategy<T> strategy) {return item -> !strategy.test(item);}
}// 产品类
class Product {private String name;private double price;private double rating;private String category;public Product(String name, double price, double rating, String category) {this.name = name;this.price = price;this.rating = rating;this.category = category;}public String getName() { return name; }public double getPrice() { return price; }public double getRating() { return rating; }public String getCategory() { return category; }@Overridepublic String toString() {return String.format("%s (%.2f元, %.1f星, %s)", name, price, rating, category);}
}// 策略工具类
class ProductStrategies {// 5. 提供常用策略的静态工厂方法public static FilterStrategy<Product> priceBelow(double maxPrice) {return product -> product.getPrice() <= maxPrice;}public static FilterStrategy<Product> minRating(double minRating) {return product -> product.getRating() >= minRating;}public static FilterStrategy<Product> inCategory(String category) {return product -> product.getCategory().equals(category);}
}
客户端代码:
public class StrategyBestPracticesDemo {public static void main(String[] args) {// 创建产品列表List<Product> products = Arrays.asList(new Product("手机A", 3999, 4.5, "电子产品"),new Product("手机B", 2499, 4.0, "电子产品"),new Product("笔记本电脑", 6999, 4.8, "电子产品"),new Product("耳机", 899, 4.3, "电子产品"),new Product("书籍A", 59, 4.5, "图书"),new Product("书籍B", 39, 3.5, "图书"),new Product("运动鞋", 499, 4.2, "服装"),new Product("T恤", 99, 3.8, "服装"));System.out.println("所有产品:");products.forEach(System.out::println);// 创建推荐过滤器RecommendationFilter<Product> filter = new RecommendationFilter<>();// 使用Lambda表达式定义匿名策略System.out.println("\n价格低于1000元的产品:");filter.setStrategy(product -> product.getPrice() < 1000);printFilteredProducts(filter.filter(products));// 使用预定义策略System.out.println("\n电子产品类别中评分4.0以上的产品:");FilterStrategy<Product> electronicStrategy = ProductStrategies.inCategory("电子产品");FilterStrategy<Product> highRatingStrategy = ProductStrategies.minRating(4.0);// 组合策略filter.setStrategy(RecommendationFilter.and(electronicStrategy, highRatingStrategy));printFilteredProducts(filter.filter(products));// 价格低于1000元或评分高于4.5的产品System.out.println("\n价格低于1000元或评分高于4.5的产品:");FilterStrategy<Product> lowPriceStrategy = ProductStrategies.priceBelow(1000);FilterStrategy<Product> veryHighRatingStrategy = ProductStrategies.minRating(4.5);filter.setStrategy(RecommendationFilter.or(lowPriceStrategy, veryHighRatingStrategy));printFilteredProducts(filter.filter(products));// 非电子产品且评分低于4.0的产品System.out.println("\n非电子产品且评分低于4.0的产品:");FilterStrategy<Product> nonElectronicStrategy = RecommendationFilter.not(electronicStrategy);FilterStrategy<Product> lowRatingStrategy = RecommendationFilter.not(ProductStrategies.minRating(4.0));filter.setStrategy(RecommendationFilter.and(nonElectronicStrategy, lowRatingStrategy));printFilteredProducts(filter.filter(products));}private static void printFilteredProducts(List<Product> products) {if (products.isEmpty()) {System.out.println("没有找到符合条件的产品");} else {products.forEach(System.out::println);}}
}
输出结果:
所有产品:
手机A (3999.00元, 4.5星, 电子产品)
手机B (2499.00元, 4.0星, 电子产品)
笔记本电脑 (6999.00元, 4.8星, 电子产品)
耳机 (899.00元, 4.3星, 电子产品)
书籍A (59.00元, 4.5星, 图书)
书籍B (39.00元, 3.5星, 图书)
运动鞋 (499.00元, 4.2星, 服装)
T恤 (99.00元, 3.8星, 服装)价格低于1000元的产品:
耳机 (899.00元, 4.3星, 电子产品)
书籍A (59.00元, 4.5星, 图书)
书籍B (39.00元, 3.5星, 图书)
运动鞋 (499.00元, 4.2星, 服装)
T恤 (99.00元, 3.8星, 服装)电子产品类别中评分4.0以上的产品:
手机A (3999.00元, 4.5星, 电子产品)
手机B (2499.00元, 4.0星, 电子产品)
笔记本电脑 (6999.00元, 4.8星, 电子产品)
耳机 (899.00元, 4.3星, 电子产品)价格低于1000元或评分高于4.5的产品:
手机A (3999.00元, 4.5星, 电子产品)
笔记本电脑 (6999.00元, 4.8星, 电子产品)
耳机 (899.00元, 4.3星, 电子产品)
书籍A (59.00元, 4.5星, 图书)
书籍B (39.00元, 3.5星, 图书)
运动鞋 (499.00元, 4.2星, 服装)
T恤 (99.00元, 3.8星, 服装)非电子产品且评分低于4.0的产品:
书籍B (39.00元, 3.5星, 图书)
T恤 (99.00元, 3.8星, 服装)
这个示例展示了几个最佳实践:
- 使用函数式接口简化策略定义
- 提供默认策略
- 支持策略组合
- 提供静态工厂方法创建常用策略
- 使用Lambda表达式创建匿名策略
13.3 性能考虑
策略模式在提供灵活性的同时,也可能带来一些性能开销。以下是一些优化策略模式性能的方法:
-
策略复用:对于无状态的策略,可以将其实现为单例或静态工厂方法,避免重复创建相同的策略对象。
-
懒加载策略:只在真正需要时才创建策略对象。
-
缓存策略结果:如果策略计算开销大,可以考虑缓存结果。
-
适当的粒度:选择合适的策略粒度,太细粒度可能导致类爆炸,太粗粒度可能失去灵活性。
// 缓存策略的实现示例
class CachingPricingStrategy implements PricingStrategy {private final PricingStrategy delegate;private final Map<String, Double> cache = new HashMap<>();public CachingPricingStrategy(PricingStrategy delegate) {this.delegate = delegate;}@Overridepublic double calculatePrice(Product product) {String key = product.getId();if (cache.containsKey(key)) {System.out.println("从缓存获取价格: " + product.getName());return cache.get(key);}System.out.println("计算价格: " + product.getName());double price = delegate.calculatePrice(product);cache.put(key, price);return price;}public void clearCache() {cache.clear();}
}
14. 总结
策略模式是一种强大且灵活的设计模式,它允许在运行时动态选择算法,使算法的变化独立于使用算法的客户端。这种模式特别适合于需要在多种算法中进行选择的场景,例如排序、验证、格式化和支付处理等。
14.1 核心要点
-
封装算法:策略模式将每个算法封装在独立的类中,使其可以独立于客户端而变化。
-
可替换性:通过定义公共接口,策略可以在运行时互相替换,而不需要修改使用它们的代码。
-
消除条件语句:策略模式有助于消除复杂的条件语句,使代码更加清晰和可维护。
-
遵循开闭原则:可以添加新的策略而不修改现有代码,符合"对扩展开放,对修改封闭"的原则。
-
组合优于继承:策略模式使用组合而非继承来实现算法的变化,更加灵活。
14.2 常见应用场景
策略模式在实际应用中非常广泛,包括但不限于:
- 排序算法:根据不同需求选择不同的排序算法
- 支付处理:处理不同的支付方式
- 验证规则:应用不同的验证规则和条件
- 数据压缩:选择不同的压缩算法
- 路由算法:选择不同的路由算法计算路径
- 文本格式化:应用不同的文本格式化规则
- 价格计算:根据不同条件计算价格和折扣
14.3 与Java 8+的结合
随着Java 8引入Lambda表达式和函数式接口,策略模式变得更加简洁和强大。你可以使用Lambda表达式创建策略,而不需要定义多个具体策略类,这使得策略模式更容易使用和维护。
例如,使用Comparator
、Predicate
、Function
等函数式接口,你可以轻松地创建和组合各种策略,而不需要编写大量的样板代码。
14.4 最终建议
- 适时使用:策略模式是一种强大的工具,但不是万能的。在引入策略模式之前,考虑是否真的需要算法的动态变化。
- 减少复杂性:避免过度使用策略模式,这可能导致系统中类的数量过多。
- 结合其他模式:策略模式通常与工厂模式、命令模式等一起使用,以提供更完整的解决方案。
通过恰当地使用策略模式,你可以创建更加灵活、可维护和可扩展的应用程序,有效地处理算法的变化,同时保持系统的整体结构简洁清晰。