策略模式 Strategy Pattern
A Beginner's Guide to the Strategy Design Pattern
https://en.wikipedia.org/wiki/Strategy_pattern
Strategy Design Pattern - GeeksforGeeks
介绍
策略模式「strategy pattern 」(也称为policy pattern)是一种行为软件设计模式「behavioral software design pattern 」,可以在运行时选择算法。代码不是直接实现单个算法,而是接收运行时指令,指示使用一系列算法「a family of algorithms」中的哪一个。
在上面的UML类图中,
Context类不直接实现算法。相反,Context引用 用于执行算法的Strategy接口(Strategy.algorithm()),这使得Context独立于算法的实现方式。Strategy1和Strategy2类实现了Strategy接口,即实现(封装)一个算法。
UML序列图显示了运行时交互:
Context对象将算法委托给不同的Strategy对象。首先,Context对Strategy1对象调用algorithm(),该对象执行算法并将结果返回给Context。此后,Context更改其策略,并在Strategy2对象上调用algorithm(),该对象执行算法并将结果返回给Context。
Strategy and open/closed principle
根据策略模式,类的行为不应该被继承。相反,它们应该使用接口封装。这与开放/封闭原则(OCP)是兼容的,OCP建议类应该对扩展开放,但对修改关闭。
例如,考虑一个汽车类。汽车的两种可能的功能是刹车「brake」和加速「accelerate」。由于加速和制动行为在模型之间经常发生变化,一种常见的方法是在子类中实现这些行为。这种方法有明显的缺点;每一款新车都必须声明加速和制动行为。随着模型数量的增加,管理这些行为的工作也会大大增加,并且需要在模型之间复制代码。此外,如果不调查每个模型中的代码,就不容易确定每个模型的行为的确切性质。
策略模式使用组合而不是继承。在策略模式中,行为被定义为单独的接口和实现这些接口的特定类。这允许行为和使用该行为的类之间更好地解耦。可以在不破坏使用它的类的情况下更改行为,并且类可以通过更改所使用的特定实现在行为之间切换,而无需进行任何重大的代码更改。行为也可以在运行时和设计时更改。例如,一个汽车对象的制动行为可以从BrakeWithABS()改变为Brake()通过改变brakeBehavior成员:
brakeBehavior = new Brake();
/* Encapsulated family of Algorithms
* Interface and its implementations
*/
public interface IBrakeBehavior {
public void brake();
}
public class BrakeWithABS implements IBrakeBehavior {
public void brake() {
System.out.println("Brake with ABS applied");
}
}
public class Brake implements IBrakeBehavior {
public void brake() {
System.out.println("Simple Brake applied");
}
}
/* Client that can use the algorithms above interchangeably */
public abstract class Car {
private IBrakeBehavior brakeBehavior;
public Car(IBrakeBehavior brakeBehavior) {
this.brakeBehavior = brakeBehavior;
}
public void applyBrake() {
brakeBehavior.brake();
}
public void setBrakeBehavior(IBrakeBehavior brakeType) {
this.brakeBehavior = brakeType;
}
}
/* Client 1 uses one algorithm (Brake) in the constructor */
public class Sedan extends Car {
public Sedan() {
super(new Brake());
}
}
/* Client 2 uses another algorithm (BrakeWithABS) in the constructor */
public class SUV extends Car {
public SUV() {
super(new BrakeWithABS());
}
}
/* Using the Car example */
public class CarExample {
public static void main(final String[] arguments) {
Car sedanCar = new Sedan();
sedanCar.applyBrake(); // This will invoke class "Brake"
Car suvCar = new SUV();
suvCar.applyBrake(); // This will invoke class "BrakeWithABS"
// set brake behavior dynamically
suvCar.setBrakeBehavior( new Brake() );
suvCar.applyBrake(); // This will invoke class "Brake"
}
}
使用策略模式的对比
package withoutstrategy;
public class PaymentProcessor {
private PaymentType paymentType;
public void processPayment(double amount) {
if (paymentType == PaymentType.CREDIT_CARD) {
System.out.println("Processing credit card payment of amount " + amount);
} else if (paymentType == PaymentType.DEBIT_CARD) {
System.out.println("Processing debit card payment of amount " + amount);
} else if (paymentType == PaymentType.PAYPAL) {
System.out.println("Processing PayPal payment of amount " + amount);
} else {
throw new IllegalArgumentException("Invalid payment type");
}
}
public void setPaymentType(PaymentType paymentType) {
this.paymentType = paymentType;
}
}
enum PaymentType {
CREDIT_CARD,
DEBIT_CARD,
PAYPAL
}
package withstrategy;
public interface PaymentStrategy {
void processPayment(double amount);
}
public class CreditCardPaymentStrategy implements PaymentStrategy {
public void processPayment(double amount) {
System.out.println("Processing credit card payment of amount " + amount);
}
}
public class DebitCardPaymentStrategy implements PaymentStrategy {
public void processPayment(double amount) {
System.out.println("Processing debit card payment of amount " + amount);
}
}
public class PaypalPaymentStrategy implements PaymentStrategy {
public void processPayment(double amount) {
System.out.println("Processing PayPal payment of amount " + amount);
}
}
public class PaymentProcessor {
private PaymentStrategy paymentStrategy;
public PaymentProcessor(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void processPayment(double amount) {
paymentStrategy.processPayment(amount);
}
}
实施战略设计模式的最佳实践
保持界面简洁,专注于单一职责。
将任何有状态的行为封装在具体的策略类中,而不是context 类中。
使用依赖注入将具体策略传递给 context 类,而不是直接在context 中创建它。
使用枚举或工厂类为创建和管理具体的策略对象提供一个集中的位置。
战略设计模式的用例
排序算法:不同的排序算法可以封装到单独的策略中,并传递给需要排序的对象。
验证规则:不同的验证规则可以封装到单独的策略中,并传递给需要验证的对象。
文本格式化:不同的格式化策略可以封装到单独的策略中,并传递给需要格式化的对象。
数据库访问:不同的数据库访问策略可以封装到单独的策略中,并传递给需要访问不同来源数据的对象。
支付策略:不同的支付方式可以封装到单独的策略中,并传递给需要处理支付的对象。
排序算法例子
// SortingContext.java
class SortingContext {
private SortingStrategy sortingStrategy;
public SortingContext(SortingStrategy sortingStrategy) {
this.sortingStrategy = sortingStrategy;
}
public void setSortingStrategy(SortingStrategy sortingStrategy) {
this.sortingStrategy = sortingStrategy;
}
public void performSort(int[] array) {
sortingStrategy.sort(array);
}
}
// SortingStrategy.java
interface SortingStrategy {
void sort(int[] array);
}
// BubbleSortStrategy.java
class BubbleSortStrategy implements SortingStrategy {
@Override
public void sort(int[] array) {
// Implement Bubble Sort algorithm
System.out.println("Sorting using Bubble Sort");
// Actual Bubble Sort Logic here
}
}
// MergeSortStrategy.java
class MergeSortStrategy implements SortingStrategy {
@Override
public void sort(int[] array) {
// Implement Merge Sort algorithm
System.out.println("Sorting using Merge Sort");
// Actual Merge Sort Logic here
}
}
// QuickSortStrategy.java
class QuickSortStrategy implements SortingStrategy {
@Override
public void sort(int[] array) {
// Implement Quick Sort algorithm
System.out.println("Sorting using Quick Sort");
// Actual Quick Sort Logic here
}
}
// Client.java
public class Client {
public static void main(String[] args) {
// Create SortingContext with BubbleSortStrategy
SortingContext sortingContext = new SortingContext(new BubbleSortStrategy());
int[] array1 = {5, 2, 9, 1, 5};
sortingContext.performSort(array1); // Output: Sorting using Bubble Sort
// Change strategy to MergeSortStrategy
sortingContext.setSortingStrategy(new MergeSortStrategy());
int[] array2 = {8, 3, 7, 4, 2};
sortingContext.performSort(array2); // Output: Sorting using Merge Sort
// Change strategy to QuickSortStrategy
sortingContext.setSortingStrategy(new QuickSortStrategy());
int[] array3 = {6, 1, 3, 9, 5};
sortingContext.performSort(array3); // Output: Sorting using Quick Sort
}
}