策略模式

2 阅读3分钟

策略模式的要点

策略模式(Strategy Pattern)是一种行为设计模式,主要用于定义一系列算法,并将每一个算法封装起来,使它们可以相互替换。其主要要点包括:

  1. 定义算法族:将算法定义为一系列可互换的策略类。
  2. 封装变化:将算法的具体实现封装在策略类中,使得算法的变化不会影响使用算法的客户端。
  3. 独立实现:每个策略类可以独立实现不同的算法,不影响其他策略。
  4. 运行时切换:客户端可以在运行时选择或切换不同的策略,而无需修改客户端代码。
  5. 遵循开闭原则:新增策略类时,无需修改现有代码,符合开闭原则(对扩展开放,对修改关闭)。

策略模式的组成部分

  1. 策略接口(Strategy):定义算法的公共接口。
  2. 具体策略类(ConcreteStrategy):实现策略接口的具体算法。
  3. 上下文类(Context):持有一个策略接口的引用,使用策略对象来完成算法。

具体例子

以下是一个简单的策略模式示例,展示不同的排序算法:

策略接口:

public interface SortStrategy {
    void sort(int[] array);
}

具体策略类:

public class BubbleSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Using Bubble Sort");
        // 冒泡排序算法实现
    }
}

public class QuickSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("Using Quick Sort");
        // 快速排序算法实现
    }
}

上下文类:

public class SortingContext {
    private SortStrategy strategy;

    public void setStrategy(SortStrategy strategy) {
        this.strategy = strategy;
    }

    public void sortArray(int[] array) {
        strategy.sort(array);
    }
}

客户端代码:

public class StrategyPatternExample {
    public static void main(String[] args) {
        int[] array = {5, 2, 9, 1, 5, 6};

        SortingContext context = new SortingContext();

        // 使用冒泡排序策略
        context.setStrategy(new BubbleSortStrategy());
        context.sortArray(array);

        // 使用快速排序策略
        context.setStrategy(new QuickSortStrategy());
        context.sortArray(array);
    }
}

在Spring中的实现

在Spring中,可以通过注解和依赖注入实现策略模式。以下是一个示例:

策略接口和具体策略类:

public interface PaymentStrategy {
    void pay(int amount);
}

@Component
public class CreditCardPayment implements PaymentStrategy {
    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using Credit Card.");
    }
}

@Component
public class PayPalPayment implements PaymentStrategy {
    @Override
    public void pay(int amount) {
        System.out.println("Paid " + amount + " using PayPal.");
    }
}

上下文类:

@Component
public class PaymentContext {
    private final Map<String, PaymentStrategy> strategies;

    @Autowired
    public PaymentContext(List<PaymentStrategy> strategyList) {
        strategies = strategyList.stream().collect(Collectors.toMap(
            s -> s.getClass().getSimpleName(), s -> s
        ));
    }

    public void pay(String strategyName, int amount) {
        PaymentStrategy strategy = strategies.get(strategyName);
        if (strategy != null) {
            strategy.pay(amount);
        } else {
            throw new IllegalArgumentException("No such payment strategy: " + strategyName);
        }
    }
}

客户端代码:

@SpringBootApplication
public class StrategyPatternSpringExample implements CommandLineRunner {

    @Autowired
    private PaymentContext paymentContext;

    public static void main(String[] args) {
        SpringApplication.run(StrategyPatternSpringExample.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        paymentContext.pay("CreditCardPayment", 100);
        paymentContext.pay("PayPalPayment", 200);
    }
}

在Java类库中的示例

  1. java.util.Comparator

    • Comparator接口本质上就是策略模式的一个应用。不同的Comparator实现可以定义不同的比较策略。
    • 示例:
      List<String> list = Arrays.asList("banana", "apple", "cherry");
      Collections.sort(list, new Comparator<String>() {
          @Override
          public int compare(String o1, String o2) {
              return o1.compareTo(o2);
          }
      });
      
  2. javax.swing

    • 在Swing中,LayoutManager接口及其实现类也应用了策略模式,不同的布局管理器实现了不同的布局策略。
    • 示例:
      JFrame frame = new JFrame();
      frame.setLayout(new BorderLayout());
      

在Angular中的示例

在Angular中,策略模式可以用来处理不同的策略,比如数据格式化、验证等。以下是一个示例:

策略接口和具体策略类:

// 策略接口
export interface FormatterStrategy {
  format(data: any): string;
}

// 具体策略类
export class JsonFormatter implements FormatterStrategy {
  format(data: any): string {
    return JSON.stringify(data, null, 2);
  }
}

export class XmlFormatter implements FormatterStrategy {
  format(data: any): string {
    // 简单的XML格式化示例
    return `<data>${data}</data>`;
  }
}

上下文类:

import { Injectable } from '@angular/core';
import { FormatterStrategy } from './formatter-strategy';

@Injectable({
  providedIn: 'root',
})
export class FormatterContext {
  private strategy: FormatterStrategy;

  setStrategy(strategy: FormatterStrategy) {
    this.strategy = strategy;
  }

  formatData(data: any): string {
    return this.strategy.format(data);
  }
}

组件使用示例:

import { Component, OnInit } from '@angular/core';
import { FormatterContext } from './formatter-context';
import { JsonFormatter } from './json-formatter';
import { XmlFormatter } from './xml-formatter';

@Component({
  selector: 'app-root',
  template: `
    <div>
      <button (click)="useJson()">Use JSON Formatter</button>
      <button (click)="useXml()">Use XML Formatter</button>
      <pre>{{ formattedData }}</pre>
    </div>
  `,
})
export class AppComponent implements OnInit {
  formattedData: string;

  constructor(private formatterContext: FormatterContext) {}

  ngOnInit() {
    this.useJson();
  }

  useJson() {
    this.formatterContext.setStrategy(new JsonFormatter());
    this.formattedData = this.formatterContext.formatData({ name: 'Angular', version: '12' });
  }

  useXml() {
    this.formatterContext.setStrategy(new XmlFormatter());
    this.formattedData = this.formatterContext.formatData('Angular 12');
  }
}

在React中的示例

在React中,策略模式可以用来处理不同的策略,比如数据格式化、验证等。以下是一个示例:

策略接口和具体策略类:

// 策略接口
class FormatterStrategy {
  format(data) {
    throw new Error("This method should be overridden.");
  }
}

// 具体策略类
class JsonFormatter extends FormatterStrategy {
  format(data) {
    return JSON.stringify(data, null, 2);
  }
}

class XmlFormatter extends FormatterStrategy {
  format(data) {
    // 简单的XML格式化示例
    return `<data>${data}</data>`;
  }
}

上下文类:

class FormatterContext {
  constructor(strategy) {
    this.strategy = strategy;
  }

  setStrategy(strategy) {
    this.strategy = strategy;
  }

  formatData(data) {
    return this.strategy.format(data);
  }
}

组件使用示例:

import React, { useState } from "react";

const formatterContext = new FormatterContext(new JsonFormatter());

function App() {
  const [formattedData, setFormattedData] = useState("");

  const useJson = () => {
    formatterContext.setStrategy(new JsonFormatter());
    setFormattedData(formatterContext.formatData({ name: "React", version: "17" }));
  };

  const useXml = () => {
    formatterContext.setStrategy(new XmlFormatter());
    setFormattedData(formatterContext.formatData("React 17"));
  };

  return (
    <div>
      <button onClick={useJson}>Use JSON Formatter</button>
      <button onClick={useXml}>Use XML Formatter</button>
      <pre>{formattedData}</pre>
    </div>
  );
}

export default App;