您现在的位置是:网站首页 > 策略模式(Strategy)的可替换算法实现文章详情

策略模式(Strategy)的可替换算法实现

策略模式是一种行为设计模式,允许在运行时选择算法的具体实现。通过定义一系列算法并将它们封装成独立的策略类,客户端可以灵活切换不同算法,而不必修改原有代码结构。这种模式特别适合处理需要频繁变更或扩展的算法逻辑场景。

策略模式的核心思想

策略模式的核心在于将算法与使用算法的上下文分离。它包含三个主要角色:

  1. Context(上下文):维护对策略对象的引用,负责将客户端请求委托给当前策略。
  2. Strategy(策略接口):定义所有具体策略必须实现的公共接口。
  3. ConcreteStrategy(具体策略):实现策略接口的具体算法类。

这种分离使得算法可以独立于客户端变化,符合开闭原则——对扩展开放,对修改关闭。

JavaScript中的策略实现

在JavaScript中,由于函数是一等公民,策略模式可以非常简洁地实现。以下是典型的实现方式:

// 策略接口(隐式)
const strategies = {
  add: (a, b) => a + b,
  subtract: (a, b) => a - b,
  multiply: (a, b) => a * b
};

// 上下文
class Calculator {
  constructor(strategy) {
    this.strategy = strategy;
  }
  
  execute(a, b) {
    return this.strategy(a, b);
  }
  
  setStrategy(strategy) {
    this.strategy = strategy;
  }
}

// 使用示例
const calc = new Calculator(strategies.add);
console.log(calc.execute(5, 3)); // 8

calc.setStrategy(strategies.multiply);
console.log(calc.execute(5, 3)); // 15

表单验证的实际案例

策略模式特别适合表单验证场景,不同字段可能需要不同的验证规则:

const validationStrategies = {
  isNonEmpty: (value, errMsg) => {
    if (value === '') return errMsg;
  },
  minLength: (value, length, errMsg) => {
    if (value.length < length) return errMsg;
  },
  isMobile: (value, errMsg) => {
    if (!/^1[3-9]\d{9}$/.test(value)) return errMsg;
  }
};

class Validator {
  constructor() {
    this.cache = [];
  }
  
  add(value, rules) {
    rules.forEach(rule => {
      const strategyArr = rule.strategy.split(':');
      const strategyName = strategyArr.shift();
      strategyArr.unshift(value);
      strategyArr.push(rule.errMsg);
      
      this.cache.push(() => 
        validationStrategies[strategyName].apply(null, strategyArr)
      );
    });
  }
  
  validate() {
    for (let i = 0; i < this.cache.length; i++) {
      const msg = this.cache[i]();
      if (msg) return msg;
    }
  }
}

// 使用示例
const validator = new Validator();
validator.add('13812345678', [
  { strategy: 'isNonEmpty', errMsg: '手机号不能为空' },
  { strategy: 'isMobile', errMsg: '手机号格式不正确' }
]);

console.log(validator.validate()); // undefined表示验证通过

电商促销策略应用

电商平台经常需要切换不同的促销策略,策略模式可以优雅地处理这种需求:

const promotionStrategies = {
  normal: originalPrice => originalPrice,
  discount: (originalPrice, discount) => originalPrice * discount,
  fullReduction: (originalPrice, condition, reduction) => {
    return originalPrice >= condition ? originalPrice - reduction : originalPrice;
  }
};

class PriceCalculator {
  constructor(strategy, ...args) {
    this.strategy = strategy;
    this.args = args;
  }
  
  calculate(price) {
    return this.strategy(price, ...this.args);
  }
}

// 使用示例
const normalPrice = new PriceCalculator(promotionStrategies.normal);
console.log(normalPrice.calculate(100)); // 100

const discountPrice = new PriceCalculator(promotionStrategies.discount, 0.8);
console.log(discountPrice.calculate(100)); // 80

const fullReductionPrice = new PriceCalculator(
  promotionStrategies.fullReduction, 
  200, 
  50
);
console.log(fullReductionPrice.calculate(250)); // 200

策略模式的进阶应用

动态策略组合

策略可以组合使用,形成更复杂的业务逻辑:

const imageProcessingStrategies = {
  grayscale: image => `Applying grayscale to ${image}`,
  resize: (image, width, height) => 
    `Resizing ${image} to ${width}x${height}`,
  watermark: (image, text) => 
    `Adding watermark "${text}" to ${image}`
};

class ImageProcessor {
  constructor() {
    this.strategies = [];
  }
  
  addStrategy(strategy, ...args) {
    this.strategies.push({ strategy, args });
  }
  
  process(image) {
    return this.strategies.reduce((result, { strategy, args }) => {
      return strategy(result, ...args);
    }, image);
  }
}

// 使用示例
const processor = new ImageProcessor();
processor.addStrategy(imageProcessingStrategies.resize, 800, 600);
processor.addStrategy(imageProcessingStrategies.grayscale);
processor.addStrategy(imageProcessingStrategies.watermark, 'Confidential');

console.log(processor.process('photo.jpg'));
// "Adding watermark "Confidential" to Resizing Applying grayscale to photo.jpg to 800x600"

策略工厂模式

结合工厂模式可以更灵活地创建策略:

class StrategyFactory {
  static createStrategy(type) {
    switch(type) {
      case 'express':
        return (weight) => weight * 5 + 15;
      case 'standard':
        return (weight) => weight * 3;
      case 'economy':
        return (weight) => weight * 2 - (weight > 10 ? 5 : 0);
      default:
        throw new Error('Unknown strategy type');
    }
  }
}

class ShippingCalculator {
  constructor(strategyType) {
    this.strategy = StrategyFactory.createStrategy(strategyType);
  }
  
  calculate(weight) {
    return this.strategy(weight);
  }
}

// 使用示例
const expressShipping = new ShippingCalculator('express');
console.log(expressShipping.calculate(5)); // 40

策略模式的优缺点

优势体现

  1. 算法复用:相同算法可以在不同上下文中复用
  2. 避免条件判断:替代大量的if-else或switch-case语句
  3. 扩展性强:新增策略不会影响现有代码
  4. 运行时切换:可以动态改变对象的行为

潜在缺点

  1. 策略类增多:每个策略都需要一个单独类/函数
  2. 客户端认知成本:需要了解不同策略的区别
  3. 通信开销:策略与上下文之间可能需要传递额外数据

与其它模式的比较

策略模式 vs 状态模式

虽然结构相似,但两者意图不同:

  • 策略模式中策略是主动选择的
  • 状态模式中状态转换通常由内部条件触发

策略模式 vs 工厂模式

工厂关注对象创建,策略关注行为委托:

// 工厂模式示例
class LoggerFactory {
  createLogger(type) {
    switch(type) {
      case 'file': return new FileLogger();
      case 'console': return new ConsoleLogger();
    }
  }
}

// 策略模式示例
const loggingStrategies = {
  file: message => saveToFile(message),
  console: message => console.log(message)
};

class Logger {
  constructor(strategy) {
    this.strategy = strategy;
  }
  
  log(message) {
    this.strategy(message);
  }
}

TypeScript中的策略模式实现

使用TypeScript可以更好地表达策略接口:

interface SortingStrategy<T> {
  sort(items: T[]): T[];
}

class QuickSortStrategy<T> implements SortingStrategy<T> {
  sort(items: T[]): T[] {
    // 快速排序实现
    return [...items].sort((a, b) => a > b ? 1 : -1);
  }
}

class MergeSortStrategy<T> implements SortingStrategy<T> {
  sort(items: T[]): T[] {
    // 归并排序实现
    if (items.length <= 1) return items;
    const mid = Math.floor(items.length / 2);
    return this.merge(
      this.sort(items.slice(0, mid)),
      this.sort(items.slice(mid))
    );
  }
  
  private merge(left: T[], right: T[]): T[] {
    // 合并逻辑
  }
}

class Sorter<T> {
  constructor(private strategy: SortingStrategy<T>) {}
  
  sort(items: T[]): T[] {
    return this.strategy.sort(items);
  }
  
  setStrategy(strategy: SortingStrategy<T>) {
    this.strategy = strategy;
  }
}

// 使用示例
const numbers = [3, 1, 4, 1, 5, 9];
const sorter = new Sorter(new QuickSortStrategy<number>());
console.log(sorter.sort(numbers));

sorter.setStrategy(new MergeSortStrategy());
console.log(sorter.sort(numbers));

前端框架中的策略模式实践

React中的策略应用

在React组件中使用策略模式处理不同渲染逻辑:

const renderStrategies = {
  list: items => (
    <ul>
      {items.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  ),
  grid: items => (
    <div className="grid">
      {items.map(item => (
        <div key={item.id} className="grid-item">{item.name}</div>
      ))}
    </div>
  ),
  table: items => (
    <table>
      <tbody>
        {items.map(item => (
          <tr key={item.id}>
            <td>{item.id}</td>
            <td>{item.name}</td>
          </tr>
        ))}
      </tbody>
    </table>
  )
};

function ItemDisplay({ items, displayType }) {
  const render = renderStrategies[displayType] || renderStrategies.list;
  return (
    <div className="item-container">
      {render(items)}
    </div>
  );
}

// 使用示例
<ItemDisplay 
  items={products} 
  displayType="grid" 
/>

Vue中的策略应用

Vue的计算属性很适合实现策略模式:

const paymentStrategies = {
  creditCard: amount => amount * 1.03,  // 3%手续费
  paypal: amount => amount + 2,        // 固定$2手续费
  bankTransfer: amount => amount       // 无手续费
};

new Vue({
  el: '#app',
  data: {
    amount: 100,
    paymentMethod: 'creditCard'
  },
  computed: {
    finalAmount() {
      return paymentStrategies[this.paymentMethod](this.amount);
    }
  }
});

性能优化中的策略选择

根据运行环境选择最优策略:

const renderingStrategies = {
  canvas: {
    render: (data) => {
      // Canvas渲染实现
      console.log(`Rendering ${data.length} items with Canvas`);
    },
    isSupported: () => !!document.createElement('canvas').getContext
  },
  svg: {
    render: (data) => {
      // SVG渲染实现
      console.log(`Rendering ${data.length} items with SVG`);
    },
    isSupported: () => true
  },
  dom: {
    render: (data) => {
      // DOM渲染实现
      console.log(`Rendering ${data.length} items with DOM`);
    },
    isSupported: () => true
  }
};

class DataRenderer {
  constructor(data) {
    this.data = data;
    this.determineBestStrategy();
  }
  
  determineBestStrategy() {
    if (this.data.length > 1000 && renderingStrategies.canvas.isSupported()) {
      this.strategy = renderingStrategies.canvas;
    } else if (this.data.length > 100 && renderingStrategies.svg.isSupported()) {
      this.strategy = renderingStrategies.svg;
    } else {
      this.strategy = renderingStrategies.dom;
    }
  }
  
  render() {
    this.strategy.render(this.data);
  }
}

// 使用示例
const largeData = new Array(1500).fill(0).map((_, i) => i);
const renderer = new DataRenderer(largeData);
renderer.render(); // 自动选择Canvas策略

测试中的策略应用

针对不同环境使用不同的测试策略:

const testingStrategies = {
  unit: {
    run: (tests) => tests.forEach(test => console.log(`Running unit test: ${test}`)),
    needsMock: true
  },
  integration: {
    run: (tests) => tests.forEach(test => console.log(`Running integration test: ${test}`)),
    needsMock: false
  },
  e2e: {
    run: (tests) => tests.forEach(test => console.log(`Running e2e test: ${test}`)),
    needsMock: false
  }
};

class TestRunner {
  constructor(strategyType) {
    this.strategy = testingStrategies[strategyType];
  }
  
  runTests(tests) {
    if (this.strategy.needsMock) {
      console.log('Setting up mocks...');
    }
    this.strategy.run(tests);
  }
}

// 使用示例
const unitTestRunner = new TestRunner('unit');
unitTestRunner.runTests(['UserService', 'AuthService']);

我的名片

网名:~川~

岗位:console.log 调试员

坐标:重庆市-九龙坡区

邮箱:cc@qdcc.cn

沙漏人生

站点信息

  • 建站时间:2013/03/16
  • 本站运行
  • 文章数量
  • 总访问量
微信公众号
每次关注
都是向财富自由迈进的一步