Skip to content

策略模式详解:概念、实现与应用

引言

策略模式是一种行为设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中,使算法的变化独立于使用它们的客户端。

什么是策略模式?

策略模式是一种行为设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中,使算法的变化独立于使用它们的客户端。策略模式让算法的变化独立于使用算法的客户。

核心思想

策略模式的核心思想是:

  1. 算法封装:将不同的算法封装在独立的类中
  2. 统一接口:为所有算法提供统一的接口
  3. 动态切换:允许在运行时选择和切换算法

为什么需要策略模式?

在许多情况下,我们需要在不同的算法之间进行选择:

1. 算法多样性

当需要实现多种算法来解决同一问题时:

  • 排序算法(快速排序、归并排序、堆排序)
  • 路径查找算法(Dijkstra、A*、BFS)
  • 压缩算法(ZIP、RAR、7Z)

2. 算法可替换性

当需要在运行时动态选择算法时:

  • 根据输入数据特征选择最优算法
  • 根据系统配置选择合适算法
  • 根据用户偏好切换算法

3. 避免条件语句

当需要避免大量的条件判断语句时:

  • 减少 if-else 或 switch-case 语句
  • 提高代码的可维护性
  • 增强系统的可扩展性

策略模式的基本实现

让我们从一个简单的策略模式实现开始:

javascript
// 策略接口
class Strategy {
  execute(data) {
    throw new Error('必须实现 execute 方法');
  }
}

// 具体策略 A
class ConcreteStrategyA extends Strategy {
  execute(data) {
    return `具体策略A处理数据: ${data.toUpperCase()}`;
  }
}

// 具体策略 B
class ConcreteStrategyB extends Strategy {
  execute(data) {
    return `具体策略B处理数据: ${data.toLowerCase()}`;
  }
}

// 具体策略 C
class ConcreteStrategyC extends Strategy {
  execute(data) {
    return `具体策略C处理数据: ${data.split('').reverse().join('')}`;
  }
}

// 上下文
class Context {
  constructor(strategy) {
    this.strategy = strategy;
  }
  
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  
  executeStrategy(data) {
    return this.strategy.execute(data);
  }
}

// 客户端代码
function clientCode() {
  const context = new Context(new ConcreteStrategyA());
  
  console.log('客户端: 使用策略A');
  console.log(context.executeStrategy('Hello World'));
  
  console.log('客户端: 切换到策略B');
  context.setStrategy(new ConcreteStrategyB());
  console.log(context.executeStrategy('Hello World'));
  
  console.log('客户端: 切换到策略C');
  context.setStrategy(new ConcreteStrategyC());
  console.log(context.executeStrategy('Hello World'));
}

clientCode();

实现要点分析

  1. 策略接口:定义所有策略的公共接口
  2. 具体策略:实现具体的算法
  3. 上下文:使用策略的类,维护对策略的引用
  4. 客户端:创建上下文并配置具体策略
  5. 动态切换:支持在运行时切换策略

策略模式的实际应用场景

1. 排序算法选择

在数据处理中,策略模式非常适合用于实现不同的排序算法:

javascript
// 排序策略接口
class SortStrategy {
  sort(data) {
    throw new Error('必须实现 sort 方法');
  }
  
  getName() {
    throw new Error('必须实现 getName 方法');
  }
}

// 冒泡排序策略
class BubbleSortStrategy extends SortStrategy {
  sort(data) {
    console.log('使用冒泡排序');
    const arr = [...data]; // 创建副本,避免修改原数组
    const n = arr.length;
    
    for (let i = 0; i < n - 1; i++) {
      for (let j = 0; j < n - i - 1; j++) {
        if (arr[j] > arr[j + 1]) {
          // 交换元素
          [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        }
      }
    }
    
    return arr;
  }
  
  getName() {
    return '冒泡排序';
  }
}

// 快速排序策略
class QuickSortStrategy extends SortStrategy {
  sort(data) {
    console.log('使用快速排序');
    const arr = [...data];
    return this.quickSort(arr, 0, arr.length - 1);
  }
  
  quickSort(arr, low, high) {
    if (low < high) {
      const pi = this.partition(arr, low, high);
      this.quickSort(arr, low, pi - 1);
      this.quickSort(arr, pi + 1, high);
    }
    return arr;
  }
  
  partition(arr, low, high) {
    const pivot = arr[high];
    let i = low - 1;
    
    for (let j = low; j < high; j++) {
      if (arr[j] < pivot) {
        i++;
        [arr[i], arr[j]] = [arr[j], arr[i]];
      }
    }
    
    [arr[i + 1], arr[high]] = [arr[high], arr[i + 1]];
    return i + 1;
  }
  
  getName() {
    return '快速排序';
  }
}

// 归并排序策略
class MergeSortStrategy extends SortStrategy {
  sort(data) {
    console.log('使用归并排序');
    const arr = [...data];
    return this.mergeSort(arr);
  }
  
  mergeSort(arr) {
    if (arr.length <= 1) {
      return arr;
    }
    
    const mid = Math.floor(arr.length / 2);
    const left = arr.slice(0, mid);
    const right = arr.slice(mid);
    
    return this.merge(this.mergeSort(left), this.mergeSort(right));
  }
  
  merge(left, right) {
    const result = [];
    let leftIndex = 0;
    let rightIndex = 0;
    
    while (leftIndex < left.length && rightIndex < right.length) {
      if (left[leftIndex] < right[rightIndex]) {
        result.push(left[leftIndex]);
        leftIndex++;
      } else {
        result.push(right[rightIndex]);
        rightIndex++;
      }
    }
    
    return result
      .concat(left.slice(leftIndex))
      .concat(right.slice(rightIndex));
  }
  
  getName() {
    return '归并排序';
  }
}

// 堆排序策略
class HeapSortStrategy extends SortStrategy {
  sort(data) {
    console.log('使用堆排序');
    const arr = [...data];
    const n = arr.length;
    
    // 构建最大堆
    for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
      this.heapify(arr, n, i);
    }
    
    // 逐个从堆顶取出元素
    for (let i = n - 1; i > 0; i--) {
      [arr[0], arr[i]] = [arr[i], arr[0]];
      this.heapify(arr, i, 0);
    }
    
    return arr;
  }
  
  heapify(arr, n, i) {
    let largest = i;
    const left = 2 * i + 1;
    const right = 2 * i + 2;
    
    if (left < n && arr[left] > arr[largest]) {
      largest = left;
    }
    
    if (right < n && arr[right] > arr[largest]) {
      largest = right;
    }
    
    if (largest !== i) {
      [arr[i], arr[largest]] = [arr[largest], arr[i]];
      this.heapify(arr, n, largest);
    }
  }
  
  getName() {
    return '堆排序';
  }
}

// 排序上下文
class SortContext {
  constructor(strategy = null) {
    this.strategy = strategy;
  }
  
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  
  sort(data) {
    if (!this.strategy) {
      throw new Error('未设置排序策略');
    }
    
    console.log(`排序上下文: 使用 ${this.strategy.getName()}`);
    const startTime = performance.now();
    const result = this.strategy.sort(data);
    const endTime = performance.now();
    
    console.log(`排序耗时: ${(endTime - startTime).toFixed(2)}ms`);
    return result;
  }
  
  // 根据数据特征自动选择最优策略
  autoSort(data) {
    let strategy;
    
    if (data.length < 10) {
      strategy = new BubbleSortStrategy();
    } else if (data.length < 1000) {
      strategy = new QuickSortStrategy();
    } else {
      strategy = new MergeSortStrategy();
    }
    
    this.setStrategy(strategy);
    return this.sort(data);
  }
}

// 排序性能测试器
class SortPerformanceTester {
  constructor() {
    this.context = new SortContext();
  }
  
  testStrategy(strategy, data, name) {
    this.context.setStrategy(strategy);
    console.log(`\n=== 测试 ${name} ===`);
    return this.context.sort(data);
  }
  
  compareStrategies(data) {
    console.log('=== 排序算法性能比较 ===');
    
    const strategies = [
      new BubbleSortStrategy(),
      new QuickSortStrategy(),
      new MergeSortStrategy(),
      new HeapSortStrategy()
    ];
    
    const results = [];
    
    for (const strategy of strategies) {
      const testData = [...data];
      const startTime = performance.now();
      strategy.sort(testData);
      const endTime = performance.now();
      
      results.push({
        name: strategy.getName(),
        time: endTime - startTime
      });
    }
    
    // 按性能排序
    results.sort((a, b) => a.time - b.time);
    
    console.log('\n性能排名:');
    results.forEach((result, index) => {
      console.log(`${index + 1}. ${result.name}: ${result.time.toFixed(2)}ms`);
    });
    
    return results;
  }
}

// 使用示例
function sortingExample() {
  const sorter = new SortContext();
  const tester = new SortPerformanceTester();
  
  console.log('=== 排序策略示例 ===');
  
  // 测试数据
  const smallData = [64, 34, 25, 12, 22, 11, 90];
  const largeData = Array.from({length: 1000}, () => Math.floor(Math.random() * 1000));
  
  console.log('原始小数据:', smallData);
  
  // 使用不同策略排序
  sorter.setStrategy(new BubbleSortStrategy());
  console.log('冒泡排序结果:', sorter.sort(smallData));
  
  sorter.setStrategy(new QuickSortStrategy());
  console.log('快速排序结果:', sorter.sort(smallData));
  
  sorter.setStrategy(new MergeSortStrategy());
  console.log('归并排序结果:', sorter.sort(smallData));
  
  console.log('\n=== 自动选择策略 ===');
  console.log('自动排序小数据:', sorter.autoSort(smallData));
  console.log('自动排序大数据:', sorter.autoSort(largeData.slice(0, 50)));
  
  console.log('\n=== 性能比较 ===');
  tester.compareStrategies(Array.from({length: 100}, () => Math.floor(Math.random() * 100)));
}

sortingExample();

2. 支付方式选择

在电商系统中,策略模式非常适合用于实现不同的支付方式:

javascript
// 支付策略接口
class PaymentStrategy {
  pay(amount) {
    throw new Error('必须实现 pay 方法');
  }
  
  getName() {
    throw new Error('必须实现 getName 方法');
  }
  
  validatePaymentInfo(paymentInfo) {
    throw new Error('必须实现 validatePaymentInfo 方法');
  }
}

// 信用卡支付策略
class CreditCardPaymentStrategy extends PaymentStrategy {
  constructor(cardNumber, expiryDate, cvv) {
    super();
    this.cardNumber = cardNumber;
    this.expiryDate = expiryDate;
    this.cvv = cvv;
  }
  
  pay(amount) {
    if (!this.validatePaymentInfo()) {
      throw new Error('信用卡信息验证失败');
    }
    
    console.log(`信用卡支付: 使用卡号 ${this.maskCardNumber()} 支付 $${amount}`);
    // 模拟支付处理
    return {
      success: true,
      transactionId: `CC_${Date.now()}`,
      message: '信用卡支付成功'
    };
  }
  
  getName() {
    return '信用卡支付';
  }
  
  validatePaymentInfo() {
    // 简单的验证逻辑
    return this.cardNumber && this.expiryDate && this.cvv &&
           this.cardNumber.length >= 15 && 
           this.cvv.length === 3;
  }
  
  maskCardNumber() {
    if (!this.cardNumber) return '';
    return '*'.repeat(this.cardNumber.length - 4) + this.cardNumber.slice(-4);
  }
}

// 支付宝支付策略
class AlipayPaymentStrategy extends PaymentStrategy {
  constructor(account) {
    super();
    this.account = account;
  }
  
  pay(amount) {
    if (!this.validatePaymentInfo()) {
      throw new Error('支付宝账户验证失败');
    }
    
    console.log(`支付宝支付: 使用账户 ${this.account} 支付 ¥${amount}`);
    // 模拟支付处理
    return {
      success: true,
      transactionId: `ALIPAY_${Date.now()}`,
      message: '支付宝支付成功',
      qrCode: `alipay://pay?amount=${amount}&account=${this.account}`
    };
  }
  
  getName() {
    return '支付宝支付';
  }
  
  validatePaymentInfo() {
    // 简单的验证逻辑
    return this.account && this.account.includes('@');
  }
}

// 微信支付策略
class WeChatPayPaymentStrategy extends PaymentStrategy {
  constructor(openId) {
    super();
    this.openId = openId;
  }
  
  pay(amount) {
    if (!this.validatePaymentInfo()) {
      throw new Error('微信账户验证失败');
    }
    
    console.log(`微信支付: 使用OpenID ${this.openId} 支付 ¥${amount}`);
    // 模拟支付处理
    return {
      success: true,
      transactionId: `WECHAT_${Date.now()}`,
      message: '微信支付成功',
      qrCode: `wechat://pay?amount=${amount}&openid=${this.openId}`
    };
  }
  
  getName() {
    return '微信支付';
  }
  
  validatePaymentInfo() {
    // 简单的验证逻辑
    return this.openId && this.openId.length > 10;
  }
}

// PayPal支付策略
class PayPalPaymentStrategy extends PaymentStrategy {
  constructor(email) {
    super();
    this.email = email;
  }
  
  pay(amount) {
    if (!this.validatePaymentInfo()) {
      throw new Error('PayPal账户验证失败');
    }
    
    console.log(`PayPal支付: 使用邮箱 ${this.email} 支付 $${amount}`);
    // 模拟支付处理
    return {
      success: true,
      transactionId: `PAYPAL_${Date.now()}`,
      message: 'PayPal支付成功',
      redirectUrl: `https://paypal.com/pay?amount=${amount}&email=${this.email}`
    };
  }
  
  getName() {
    return 'PayPal支付';
  }
  
  validatePaymentInfo() {
    // 简单的验证逻辑
    return this.email && this.email.includes('@') && this.email.includes('.');
  }
}

// 现金支付策略
class CashPaymentStrategy extends PaymentStrategy {
  pay(amount) {
    console.log(`现金支付: 收到现金 $${amount}`);
    // 模拟支付处理
    return {
      success: true,
      transactionId: `CASH_${Date.now()}`,
      message: '现金支付成功',
      change: 0
    };
  }
  
  getName() {
    return '现金支付';
  }
  
  validatePaymentInfo() {
    return true; // 现金支付无需验证
  }
}

// 支付上下文
class PaymentContext {
  constructor(strategy = null) {
    this.strategy = strategy;
    this.transactionHistory = [];
  }
  
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  
  executePayment(amount, paymentInfo = null) {
    if (!this.strategy) {
      throw new Error('未设置支付策略');
    }
    
    console.log(`支付上下文: 使用 ${this.strategy.getName()} 进行支付`);
    
    try {
      const result = this.strategy.pay(amount);
      if (result.success) {
        this.transactionHistory.push({
          strategy: this.strategy.getName(),
          amount: amount,
          transactionId: result.transactionId,
          timestamp: new Date(),
          ...result
        });
        console.log(`支付成功: ${result.message}`);
        return result;
      } else {
        throw new Error(result.message || '支付失败');
      }
    } catch (error) {
      console.error(`支付失败: ${error.message}`);
      throw error;
    }
  }
  
  getTransactionHistory() {
    return [...this.transactionHistory];
  }
  
  getTotalAmount() {
    return this.transactionHistory.reduce((total, transaction) => total + transaction.amount, 0);
  }
  
  // 根据支付金额自动选择策略
  autoPay(amount, preferredMethod = null) {
    let strategy;
    
    // 根据金额和偏好选择策略
    if (amount <= 0) {
      throw new Error('支付金额必须大于0');
    }
    
    if (preferredMethod) {
      strategy = this.createStrategyByMethod(preferredMethod);
    } else if (amount < 1) {
      strategy = new CashPaymentStrategy();
    } else if (amount < 100) {
      strategy = new WeChatPayPaymentStrategy('openid_12345');
    } else {
      strategy = new CreditCardPaymentStrategy('1234567890123456', '12/25', '123');
    }
    
    this.setStrategy(strategy);
    return this.executePayment(amount);
  }
  
  createStrategyByMethod(method, paymentInfo = {}) {
    switch (method.toLowerCase()) {
      case 'creditcard':
        return new CreditCardPaymentStrategy(
          paymentInfo.cardNumber || '1234567890123456',
          paymentInfo.expiryDate || '12/25',
          paymentInfo.cvv || '123'
        );
      case 'alipay':
        return new AlipayPaymentStrategy(paymentInfo.account || 'user@example.com');
      case 'wechat':
        return new WeChatPayPaymentStrategy(paymentInfo.openId || 'openid_12345');
      case 'paypal':
        return new PayPalPaymentStrategy(paymentInfo.email || 'user@example.com');
      case 'cash':
        return new CashPaymentStrategy();
      default:
        throw new Error(`不支持的支付方式: ${method}`);
    }
  }
}

// 支付管理器
class PaymentManager {
  constructor() {
    this.context = new PaymentContext();
  }
  
  processPayment(amount, method, paymentInfo = {}) {
    try {
      const strategy = this.context.createStrategyByMethod(method, paymentInfo);
      this.context.setStrategy(strategy);
      return this.context.executePayment(amount, paymentInfo);
    } catch (error) {
      console.error(`支付处理失败: ${error.message}`);
      throw error;
    }
  }
  
  getPaymentStatistics() {
    const history = this.context.getTransactionHistory();
    const stats = {
      totalTransactions: history.length,
      totalAmount: this.context.getTotalAmount(),
      paymentMethods: {}
    };
    
    // 统计各支付方式的使用情况
    for (const transaction of history) {
      const method = transaction.strategy;
      if (!stats.paymentMethods[method]) {
        stats.paymentMethods[method] = { count: 0, amount: 0 };
      }
      stats.paymentMethods[method].count++;
      stats.paymentMethods[method].amount += transaction.amount;
    }
    
    return stats;
  }
  
  refund(transactionId, amount) {
    const history = this.context.getTransactionHistory();
    const transaction = history.find(t => t.transactionId === transactionId);
    
    if (!transaction) {
      throw new Error(`未找到交易记录: ${transactionId}`);
    }
    
    if (amount > transaction.amount) {
      throw new Error('退款金额不能超过原交易金额');
    }
    
    console.log(`退款: 交易 ${transactionId} 退款 $${amount}`);
    return {
      success: true,
      refundId: `REFUND_${Date.now()}`,
      message: '退款成功'
    };
  }
}

// 使用示例
function paymentExample() {
  const paymentManager = new PaymentManager();
  
  console.log('=== 支付策略示例 ===');
  
  try {
    // 信用卡支付
    console.log('\n=== 信用卡支付 ===');
    const creditCardResult = paymentManager.processPayment(100, 'creditcard', {
      cardNumber: '1234567890123456',
      expiryDate: '12/25',
      cvv: '123'
    });
    console.log('支付结果:', creditCardResult);
    
    // 支付宝支付
    console.log('\n=== 支付宝支付 ===');
    const alipayResult = paymentManager.processPayment(50, 'alipay', {
      account: 'user@example.com'
    });
    console.log('支付结果:', alipayResult);
    
    // 微信支付
    console.log('\n=== 微信支付 ===');
    const wechatResult = paymentManager.processPayment(30, 'wechat', {
      openId: 'openid_12345'
    });
    console.log('支付结果:', wechatResult);
    
    // PayPal支付
    console.log('\n=== PayPal支付 ===');
    const paypalResult = paymentManager.processPayment(75, 'paypal', {
      email: 'user@example.com'
    });
    console.log('支付结果:', paypalResult);
    
    // 现金支付
    console.log('\n=== 现金支付 ===');
    const cashResult = paymentManager.processPayment(5, 'cash');
    console.log('支付结果:', cashResult);
    
    console.log('\n=== 自动支付选择 ===');
    const autoResult1 = paymentManager.context.autoPay(0.5); // 现金
    console.log('小额支付:', autoResult1);
    
    const autoResult2 = paymentManager.context.autoPay(50); // 微信
    console.log('中额支付:', autoResult2);
    
    const autoResult3 = paymentManager.context.autoPay(500); // 信用卡
    console.log('大额支付:', autoResult3);
    
    console.log('\n=== 交易统计 ===');
    const stats = paymentManager.getPaymentStatistics();
    console.log('统计信息:', JSON.stringify(stats, null, 2));
    
    console.log('\n=== 退款处理 ===');
    const refundResult = paymentManager.refund(creditCardResult.transactionId, 50);
    console.log('退款结果:', refundResult);
    
  } catch (error) {
    console.error('支付示例出错:', error.message);
  }
}

// 运行示例
paymentExample();

3. 路径规划算法

在导航系统中,策略模式非常适合用于实现不同的路径规划算法:

javascript
// 路径规划策略接口
class RouteStrategy {
  calculateRoute(start, end, options = {}) {
    throw new Error('必须实现 calculateRoute 方法');
  }
  
  getName() {
    throw new Error('必须实现 getName 方法');
  }
  
  getEstimatedTime(distance, options = {}) {
    throw new Error('必须实现 getEstimatedTime 方法');
  }
}

// 最短距离策略
class ShortestDistanceStrategy extends RouteStrategy {
  calculateRoute(start, end, options = {}) {
    console.log('使用最短距离算法计算路径');
    
    // 模拟路径计算
    const distance = this.calculateDistance(start, end);
    const time = this.getEstimatedTime(distance, options);
    const route = this.generateRoute(start, end);
    
    return {
      strategy: this.getName(),
      route: route,
      distance: distance,
      estimatedTime: time,
      details: '选择最短路径,可能涉及较多红绿灯'
    };
  }
  
  getName() {
    return '最短距离';
  }
  
  calculateDistance(start, end) {
    // 简化的距离计算(欧几里得距离)
    const dx = end.x - start.x;
    const dy = end.y - start.y;
    return Math.sqrt(dx * dx + dy * dy);
  }
  
  getEstimatedTime(distance, options = {}) {
    const speed = options.speed || 40; // 默认40公里/小时
    return (distance / speed) * 60; // 返回分钟
  }
  
  generateRoute(start, end) {
    // 简化的路径生成
    return [
      { x: start.x, y: start.y, name: '起点' },
      { x: (start.x + end.x) / 2, y: (start.y + end.y) / 2, name: '中点' },
      { x: end.x, y: end.y, name: '终点' }
    ];
  }
}

// 最快速度策略
class FastestRouteStrategy extends RouteStrategy {
  calculateRoute(start, end, options = {}) {
    console.log('使用最快速度算法计算路径');
    
    // 模拟路径计算(考虑道路限速)
    const distance = this.calculateDistance(start, end);
    const time = this.getEstimatedTime(distance, options);
    const route = this.generateRoute(start, end);
    
    return {
      strategy: this.getName(),
      route: route,
      distance: distance,
      estimatedTime: time,
      details: '优先选择高速公路,避免红绿灯'
    };
  }
  
  getName() {
    return '最快速度';
  }
  
  calculateDistance(start, end) {
    // 考虑实际道路距离
    const directDistance = Math.sqrt(
      Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)
    );
    return directDistance * 1.2; // 实际道路距离通常更长
  }
  
  getEstimatedTime(distance, options = {}) {
    const speed = options.speed || 60; // 高速公路默认60公里/小时
    return (distance / speed) * 60;
  }
  
  generateRoute(start, end) {
    // 优先选择高速公路
    return [
      { x: start.x, y: start.y, name: '起点' },
      { x: start.x + 5, y: start.y + 5, name: '高速公路入口' },
      { x: end.x - 5, y: end.y - 5, name: '高速公路出口' },
      { x: end.x, y: end.y, name: '终点' }
    ];
  }
}

// 最少收费策略
class LeastTollStrategy extends RouteStrategy {
  calculateRoute(start, end, options = {}) {
    console.log('使用最少收费算法计算路径');
    
    const distance = this.calculateDistance(start, end);
    const time = this.getEstimatedTime(distance, options);
    const route = this.generateRoute(start, end);
    const toll = this.calculateToll(route);
    
    return {
      strategy: this.getName(),
      route: route,
      distance: distance,
      estimatedTime: time,
      toll: toll,
      details: '避免收费道路,可能增加行驶时间'
    };
  }
  
  getName() {
    return '最少收费';
  }
  
  calculateDistance(start, end) {
    const directDistance = Math.sqrt(
      Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)
    );
    return directDistance * 1.3; // 避开收费道路可能更绕远
  }
  
  getEstimatedTime(distance, options = {}) {
    const speed = options.speed || 35; // 普通道路默认35公里/小时
    return (distance / speed) * 60;
  }
  
  generateRoute(start, end) {
    // 避开收费道路
    return [
      { x: start.x, y: start.y, name: '起点' },
      { x: start.x + 2, y: start.y + 8, name: '省道1号' },
      { x: end.x - 2, y: end.y - 8, name: '省道2号' },
      { x: end.x, y: end.y, name: '终点' }
    ];
  }
  
  calculateToll(route) {
    // 计算过路费(假设有部分路段收费)
    return route.length > 3 ? 15.5 : 0;
  }
}

// 步行路径策略
class WalkingRouteStrategy extends RouteStrategy {
  calculateRoute(start, end, options = {}) {
    console.log('使用步行路径算法计算路径');
    
    const distance = this.calculateDistance(start, end);
    const time = this.getEstimatedTime(distance, options);
    const route = this.generateRoute(start, end);
    
    return {
      strategy: this.getName(),
      route: route,
      distance: distance,
      estimatedTime: time,
      details: '适合步行的路径,避开高速公路'
    };
  }
  
  getName() {
    return '步行路径';
  }
  
  calculateDistance(start, end) {
    const directDistance = Math.sqrt(
      Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)
    );
    return directDistance;
  }
  
  getEstimatedTime(distance, options = {}) {
    const speed = options.speed || 5; // 步行默认5公里/小时
    return (distance / speed) * 60;
  }
  
  generateRoute(start, end) {
    // 生成适合步行的路径
    return [
      { x: start.x, y: start.y, name: '起点' },
      { x: start.x + 1, y: start.y + 1, name: '人行道1' },
      { x: start.x + 2, y: start.y + 2, name: '公园路径' },
      { x: end.x - 1, y: end.y - 1, name: '人行道2' },
      { x: end.x, y: end.y, name: '终点' }
    ];
  }
}

// 骑行路径策略
class CyclingRouteStrategy extends RouteStrategy {
  calculateRoute(start, end, options = {}) {
    console.log('使用骑行路径算法计算路径');
    
    const distance = this.calculateDistance(start, end);
    const time = this.getEstimatedTime(distance, options);
    const route = this.generateRoute(start, end);
    
    return {
      strategy: this.getName(),
      route: route,
      distance: distance,
      estimatedTime: time,
      details: '自行车友好路径,避开高速公路和复杂路段'
    };
  }
  
  getName() {
    return '骑行路径';
  }
  
  calculateDistance(start, end) {
    const directDistance = Math.sqrt(
      Math.pow(end.x - start.x, 2) + Math.pow(end.y - start.y, 2)
    );
    return directDistance * 1.1; // 骑行路径可能稍长
  }
  
  getEstimatedTime(distance, options = {}) {
    const speed = options.speed || 15; // 骑行默认15公里/小时
    return (distance / speed) * 60;
  }
  
  generateRoute(start, end) {
    // 生成自行车友好路径
    return [
      { x: start.x, y: start.y, name: '起点' },
      { x: start.x + 3, y: start.y + 1, name: '自行车道1' },
      { x: start.x + 6, y: start.y + 2, name: '绿道' },
      { x: end.x - 3, y: end.y - 1, name: '自行车道2' },
      { x: end.x, y: end.y, name: '终点' }
    ];
  }
}

// 路径规划上下文
class RouteContext {
  constructor(strategy = null) {
    this.strategy = strategy;
    this.routeHistory = [];
  }
  
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  
  calculateRoute(start, end, options = {}) {
    if (!this.strategy) {
      throw new Error('未设置路径规划策略');
    }
    
    console.log(`路径规划上下文: 使用 ${this.strategy.getName()} 算法`);
    
    const result = this.strategy.calculateRoute(start, end, options);
    this.routeHistory.push({
      ...result,
      start: start,
      end: end,
      timestamp: new Date()
    });
    
    return result;
  }
  
  getRouteHistory() {
    return [...this.routeHistory];
  }
  
  // 根据出行方式自动选择策略
  autoPlanRoute(start, end, travelMode, options = {}) {
    let strategy;
    
    switch (travelMode.toLowerCase()) {
      case 'driving':
        strategy = new FastestRouteStrategy();
        break;
      case 'walking':
        strategy = new WalkingRouteStrategy();
        break;
      case 'cycling':
        strategy = new CyclingRouteStrategy();
        break;
      default:
        strategy = new FastestRouteStrategy();
    }
    
    this.setStrategy(strategy);
    return this.calculateRoute(start, end, options);
  }
  
  // 比较不同策略的结果
  compareRoutes(start, end, options = {}) {
    const strategies = [
      new ShortestDistanceStrategy(),
      new FastestRouteStrategy(),
      new LeastTollStrategy(),
      new WalkingRouteStrategy(),
      new CyclingRouteStrategy()
    ];
    
    const results = [];
    
    for (const strategy of strategies) {
      this.setStrategy(strategy);
      try {
        const result = this.calculateRoute(start, end, options);
        results.push(result);
      } catch (error) {
        console.error(`策略 ${strategy.getName()} 执行失败:`, error.message);
      }
    }
    
    return results;
  }
}

// 导航系统
class NavigationSystem {
  constructor() {
    this.context = new RouteContext();
  }
  
  planRoute(start, end, strategyName, options = {}) {
    let strategy;
    
    switch (strategyName.toLowerCase()) {
      case 'shortest':
        strategy = new ShortestDistanceStrategy();
        break;
      case 'fastest':
        strategy = new FastestRouteStrategy();
        break;
      case 'leasttoll':
        strategy = new LeastTollStrategy();
        break;
      case 'walking':
        strategy = new WalkingRouteStrategy();
        break;
      case 'cycling':
        strategy = new CyclingRouteStrategy();
        break;
      default:
        throw new Error(`不支持的路径规划策略: ${strategyName}`);
    }
    
    this.context.setStrategy(strategy);
    return this.context.calculateRoute(start, end, options);
  }
  
  getRouteRecommendation(start, end, preferences = {}) {
    console.log('=== 路径规划建议 ===');
    
    const results = this.context.compareRoutes(start, end, preferences);
    
    // 根据偏好排序结果
    results.sort((a, b) => {
      // 如果用户偏好最快路径
      if (preferences.priority === 'time') {
        return a.estimatedTime - b.estimatedTime;
      }
      // 如果用户偏好最短路径
      else if (preferences.priority === 'distance') {
        return a.distance - b.distance;
      }
      // 默认按时间排序
      else {
        return a.estimatedTime - b.estimatedTime;
      }
    });
    
    return results;
  }
  
  getNavigationStats() {
    const history = this.context.getRouteHistory();
    const stats = {
      totalRoutes: history.length,
      totalDistance: history.reduce((sum, route) => sum + route.distance, 0),
      strategiesUsed: {}
    };
    
    // 统计各策略的使用情况
    for (const route of history) {
      const strategy = route.strategy;
      if (!stats.strategiesUsed[strategy]) {
        stats.strategiesUsed[strategy] = 0;
      }
      stats.strategiesUsed[strategy]++;
    }
    
    return stats;
  }
}

// 使用示例
function navigationExample() {
  const navigation = new NavigationSystem();
  
  console.log('=== 路径规划策略示例 ===');
  
  const start = { x: 0, y: 0, name: '当前位置' };
  const end = { x: 20, y: 20, name: '目的地' };
  
  try {
    // 最短距离路径
    console.log('\n=== 最短距离路径 ===');
    const shortestRoute = navigation.planRoute(start, end, 'shortest');
    console.log('路径信息:', JSON.stringify(shortestRoute, null, 2));
    
    // 最快速度路径
    console.log('\n=== 最快速度路径 ===');
    const fastestRoute = navigation.planRoute(start, end, 'fastest');
    console.log('路径信息:', JSON.stringify(fastestRoute, null, 2));
    
    // 最少收费路径
    console.log('\n=== 最少收费路径 ===');
    const leastTollRoute = navigation.planRoute(start, end, 'leasttoll');
    console.log('路径信息:', JSON.stringify(leastTollRoute, null, 2));
    
    // 步行路径
    console.log('\n=== 步行路径 ===');
    const walkingRoute = navigation.planRoute(start, end, 'walking');
    console.log('路径信息:', JSON.stringify(walkingRoute, null, 2));
    
    // 骑行路径
    console.log('\n=== 骑行路径 ===');
    const cyclingRoute = navigation.planRoute(start, end, 'cycling');
    console.log('路径信息:', JSON.stringify(cyclingRoute, null, 2));
    
    console.log('\n=== 路径建议 ===');
    const recommendations = navigation.getRouteRecommendation(start, end, { priority: 'time' });
    console.log('推荐路径:');
    recommendations.slice(0, 3).forEach((route, index) => {
      console.log(`${index + 1}. ${route.strategy}: ${route.distance.toFixed(2)}km, 预计${route.estimatedTime.toFixed(0)}分钟`);
    });
    
    console.log('\n=== 导航统计 ===');
    const stats = navigation.getNavigationStats();
    console.log('统计信息:', JSON.stringify(stats, null, 2));
    
  } catch (error) {
    console.error('路径规划示例出错:', error.message);
  }
}

// 运行示例
navigationExample();

策略模式的关键要点

1. 策略接口设计

javascript
// 策略接口应该简单明确
class Strategy {
  // 通常只包含一个主要方法
  execute(data) {
    // 核心算法逻辑
  }
  
  // 可以包含辅助方法
  validate(data) {
    // 验证输入数据
  }
}

2. 上下文设计

javascript
class Context {
  constructor(strategy) {
    this.strategy = strategy;
  }
  
  // 设置新策略
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  
  // 执行策略
  execute(data) {
    return this.strategy.execute(data);
  }
}

3. 策略选择

javascript
class StrategySelector {
  // 根据条件选择策略
  selectStrategy(condition) {
    if (condition === 'fast') {
      return new FastStrategy();
    } else if (condition === 'cheap') {
      return new CheapStrategy();
    } else {
      return new DefaultStrategy();
    }
  }
}

策略模式与其它模式的对比

策略模式 vs 状态模式

javascript
// 策略模式 - 算法选择
class StrategyContext {
  setStrategy(strategy) {
    this.strategy = strategy;
  }
  
  execute() {
    return this.strategy.execute();
  }
}

// 状态模式 - 对象状态变化
class StateContext {
  setState(state) {
    this.state = state;
  }
  
  request() {
    this.state.handle(this);
  }
}

策略模式 vs 命令模式

javascript
// 策略模式 - 算法封装
class Strategy {
  execute(data) {
    // 执行算法
  }
}

// 命令模式 - 请求封装
class Command {
  execute() {
    // 执行请求
  }
  
  undo() {
    // 撤销请求
  }
}

策略模式的优缺点

优点

  1. 算法可替换:可以在运行时切换算法
  2. 避免条件语句:减少 if-else 或 switch-case 语句
  3. 符合开闭原则:增加新算法无需修改现有代码
  4. 算法复用:不同上下文可以复用相同算法
  5. 测试友好:每个算法可以独立测试

缺点

  1. 对象数量增加:每种算法都需要一个策略类
  2. 客户端了解策略:客户端需要知道所有策略的区别
  3. 通信开销:策略对象可能传递大量数据
  4. 复杂性增加:简单的算法可能不需要策略模式

总结

策略模式是一种行为设计模式,它能让你定义一系列算法,并将每种算法分别放入独立的类中,使算法的变化独立于使用它们的客户端。策略模式让算法的变化独立于使用算法的客户。

通过本章的学习,我们了解了:

  1. 策略模式的基本概念和核心思想
  2. 策略模式的实现方式和关键要点
  3. 策略模式在实际开发中的应用场景(排序算法、支付方式、路径规划)
  4. 策略模式与其他行为型模式的对比
  5. 策略模式的优缺点

策略模式在现代软件开发中应用广泛,特别是在需要实现算法多样性、支持运行时算法切换、避免大量条件判断语句的场景中,它可以很好地支持系统的灵活性和可维护性。

在下一章中,我们将继续探讨其他行为型模式,接下来是模板方法模式。