策略模式核心概念

  1. 策略模式 (Strategy Pattern) 是一种行为型设计模式,核心思想是:定义一系列算法,把它们一个个封装起来,并且使它们可以互相替换;
  2. 类图:
  3. 实现代码:
    // 1. 抽象策略类(Strategy):定义折扣计算的公共接口
    interface DiscountStrategy {
      /**
       * 计算折扣后金额
      * @param amount 订单原价
      * @returns 折扣后金额
      */
      calculate(amount: number): number;
    }
    
    // 2. 具体策略类 A:普通用户(无折扣)
    class NormalUserStrategy implements DiscountStrategy {
      calculate(amount: number): number {
        return amount; // 原价返回
      }
    }
    
    // 2. 具体策略类 B:VIP用户(9折)
    class VipUserStrategy implements DiscountStrategy {
      calculate(amount: number): number {
        return amount * 0.9;
      }
    }
    
    // 2. 具体策略类 C:超级VIP用户(8折 + 满100减20)
    class SuperVipUserStrategy implements DiscountStrategy {
      calculate(amount: number): number {
        let finalAmount = amount * 0.8;
        // 满100减20
        if (finalAmount >= 100) {
          finalAmount -= 20;
        }
        return finalAmount;
      }
    }
    
    // 3. 环境类(Context):订单处理类,委托策略计算折扣
    class OrderContext {
      private strategy: DiscountStrategy;
    
      // 初始化时传入具体策略
      constructor(strategy: DiscountStrategy) {
        this.strategy = strategy;
      }
    
      // 动态切换策略
      setStrategy(strategy: DiscountStrategy): void {
        this.strategy = strategy;
      }
    
      // 执行策略(计算最终金额)
      calculateFinalAmount(amount: number): number {
        return this.strategy.calculate(amount);
      }
    }
    
    // 测试代码
    const orderAmount = 200; // 订单原价200元
    
    // 普通用户订单
    const normalOrder = new OrderContext(new NormalUserStrategy());
    console.log("普通用户最终金额:", normalOrder.calculateFinalAmount(orderAmount)); // 输出:200
    
    // 切换为VIP用户策略
    normalOrder.setStrategy(new VipUserStrategy());
    console.log("VIP用户最终金额:", normalOrder.calculateFinalAmount(orderAmount)); // 输出:180
    
    // 切换为超级VIP用户策略
    normalOrder.setStrategy(new SuperVipUserStrategy());
    console.log("超级VIP用户最终金额:", normalOrder.calculateFinalAmount(orderAmount)); // 输出:140(200*0.8=160,满100减20 → 140)
    

策略模式的常用场景

数据格式化

  1. 不同场景下数据格式要求不同 (如日期、金额、数字),用策略模式封装格式化逻辑:
    1. 日期:有的场景需要 YYYY-MM-DD,有的需要 MM/DD/YYYY
    2. 金额:有的需要保留 2 位小数,有的需要显示为千分位 (如 1000 → 1,000)
  2. 实现代码:
    // 1. 抽象策略:数据格式化接口
    interface FormatStrategy {
      format(value: any): string;
    }
    
    // 2. 具体策略1:日期格式化 - YYYY-MM-DD
    class DateYMDFormat implements FormatStrategy {
      format(date: Date): string {
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0');
        const day = String(date.getDate()).padStart(2, '0');
        return `${year}-${month}-${day}`;
      }
    }
    
    // 2. 具体策略2:日期格式化 - MM/DD/YYYY
    class DateMDYFormat implements FormatStrategy {
      format(date: Date): string {
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0');
        const day = String(date.getDate()).padStart(2, '0');
        return `${month}/${day}/${year}`;
      }
    }
    
    // 2. 具体策略3:金额格式化 - 千分位 + 保留2位小数
    class MoneyThousandFormat implements FormatStrategy {
      format(amount: number): string {
        return amount.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
      }
    }
    
    // 3. 环境类:格式化工具
    class FormatterContext {
      private strategy: FormatStrategy;
    
      constructor(strategy: FormatStrategy) {
        this.strategy = strategy;
      }
    
      setStrategy(strategy: FormatStrategy) {
        this.strategy = strategy;
      }
    
      executeFormat(value: any): string {
        return this.strategy.format(value);
      }
    }
    
    // 测试
    const formatter = new FormatterContext(new DateYMDFormat());
    console.log("日期(YYYY-MM-DD):", formatter.executeFormat(new Date(2026, 1, 26))); // 2026-02-26
    
    formatter.setStrategy(new DateMDYFormat());
    console.log("日期(MM/DD/YYYY):", formatter.executeFormat(new Date(2026, 1, 26))); // 02/26/2026
    
    formatter.setStrategy(new MoneyThousandFormat());
    console.log("金额(千分位):", formatter.executeFormat(123456.789)); // 123,456.79
    

支付方式选择

  1. 电商支付环节,不同支付方式 (微信、支付宝、银行卡) 的支付逻辑不同,用策略模式封装:
    1. 每种支付方式对应一个「支付策略类」,包含 pay 方法;
    2. 支付页面 (环境类) 根据用户选择,切换对应的支付策略,调用 pay 方法;
  2. 实现代码:
    // 1. 抽象策略:支付接口
    interface PayStrategy {
      pay(amount: number): Promise<string>; // 支付方法,返回支付结果
    }
    
    // 2. 具体策略1:微信支付
    class WxPayStrategy implements PayStrategy {
      async pay(amount: number): Promise<string> {
        // 模拟微信支付接口调用
        await new Promise(resolve => setTimeout(resolve, 500));
        return `微信支付成功,金额:${amount}元`;
      }
    }
    
    // 2. 具体策略2:支付宝支付
    class AliPayStrategy implements PayStrategy {
      async pay(amount: number): Promise<string> {
        // 模拟支付宝支付接口调用
        await new Promise(resolve => setTimeout(resolve, 500));
        return `支付宝支付成功,金额:${amount}元`;
      }
    }
    
    // 2. 具体策略3:银行卡支付
    class BankPayStrategy implements PayStrategy {
      async pay(amount: number): Promise<string> {
        // 模拟银行卡支付接口调用
        await new Promise(resolve => setTimeout(resolve, 500));
        return `银行卡支付成功,金额:${amount}元`;
      }
    }
    
    // 3. 环境类:支付管理器
    class PaymentContext {
      private strategy: PayStrategy;
    
      constructor(strategy: PayStrategy) {
        this.strategy = strategy;
      }
    
      changePayMethod(strategy: PayStrategy) {
        this.strategy = strategy;
      }
    
      async doPay(amount: number): Promise<string> {
        return this.strategy.pay(amount);
      }
    }
    
    // 测试
    (async () => {
      const payment = new PaymentContext(new WxPayStrategy());
      console.log(await payment.doPay(100)); // 微信支付成功,金额:100元
    
      payment.changePayMethod(new AliPayStrategy());
      console.log(await payment.doPay(200)); // 支付宝支付成功,金额:200元
    
      payment.changePayMethod(new BankPayStrategy());
      console.log(await payment.doPay(300)); // 银行卡支付成功,金额:300元
    })();
    

动画 / 样式切换

  1. 前端组件的动画效果 (如弹窗的淡入、滑入)、主题样式 (浅色、深色、自定义主题),可通过策略模式封装:
    1. 每种动画 / 样式对应一个策略类,包含 apply 方法;
    2. 组件 (环境类) 根据配置切换策略,执行 apply 方法应用动画 / 样式。
  2. 实现代码:
    // 1. 抽象策略:动画策略接口
    interface AnimationStrategy {
      apply(element: HTMLElement): void; // 给DOM元素应用动画
    }
    
    // 2. 具体策略1:淡入动画
    class FadeInAnimation implements AnimationStrategy {
      apply(element: HTMLElement): void {
        element.style.opacity = '0';
        element.style.transition = 'opacity 0.5s ease';
        setTimeout(() => {
          element.style.opacity = '1';
        }, 0);
      }
    }
    
    // 2. 具体策略2:滑入动画(从下往上)
    class SlideInAnimation implements AnimationStrategy {
      apply(element: HTMLElement): void {
        element.style.transform = 'translateY(100px)';
        element.style.opacity = '0';
        element.style.transition = 'all 0.5s ease';
        setTimeout(() => {
          element.style.transform = 'translateY(0)';
          element.style.opacity = '1';
        }, 0);
      }
    }
    
    // 2. 具体策略3:缩放动画
    class ScaleAnimation implements AnimationStrategy {
      apply(element: HTMLElement): void {
        element.style.transform = 'scale(0.8)';
        element.style.opacity = '0';
        element.style.transition = 'all 0.5s ease';
        setTimeout(() => {
          element.style.transform = 'scale(1)';
          element.style.opacity = '1';
        }, 0);
      }
    }
    
    // 3. 环境类:弹窗组件
    class ModalContext {
      private element: HTMLElement;
      private animationStrategy: AnimationStrategy;
    
      constructor(elementId: string, strategy: AnimationStrategy) {
        this.element = document.getElementById(elementId)!;
        this.animationStrategy = strategy;
      }
    
      setAnimation(strategy: AnimationStrategy) {
        this.animationStrategy = strategy;
      }
    
      show() {
        this.element.style.display = 'block';
        this.animationStrategy.apply(this.element);
      }
    }
    
    // 测试(需在浏览器环境运行,先创建id为modal的DOM元素)
    // <div id="modal" style="display: none; width: 200px; height: 200px; background: #f0f0f0; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%);"></div>
    // const modal = new ModalContext('modal', new FadeInAnimation());
    // modal.show(); // 淡入显示
    // modal.setAnimation(new SlideInAnimation());
    // modal.show(); // 滑入显示
    // modal.setAnimation(new ScaleAnimation());
    // modal.show(); // 缩放显示
    

网络请求适配

  1. 不同环境下的网络请求逻辑不同 (如开发环境走 mock 接口,生产环境走真实接口,测试环境走测试接口),用策略模式封装请求策略:
    1. 每种环境对应一个「请求策略类」,包含 request 方法;
    2. 请求工具类 (环境类) 根据环境变量切换策略,统一对外提供 request 方法;
  2. 实现代码:
    // 1. 抽象策略:请求策略接口
    interface RequestStrategy {
      request(url: string, data?: any): Promise<any>;
    }
    
    // 2. 具体策略1:开发环境 - Mock请求
    class DevRequestStrategy implements RequestStrategy {
      async request(url: string, data?: any): Promise<any> {
        // 模拟Mock数据返回
        const mockData = {
          code: 200,
          msg: 'success',
          data: { mock: true, url, data }
        };
        await new Promise(resolve => setTimeout(resolve, 300));
        return mockData;
      }
    }
    
    // 2. 具体策略2:测试环境 - 测试接口请求
    class TestRequestStrategy implements RequestStrategy {
      async request(url: string, data?: any): Promise<any> {
        // 模拟测试接口请求(拼接测试域名)
        const testUrl = `https://test-api.com${url}`;
        await new Promise(resolve => setTimeout(resolve, 500));
        return {
          code: 200,
          msg: 'success',
          data: { env: 'test', url: testUrl, data }
        };
      }
    }
    
    // 2. 具体策略3:生产环境 - 生产接口请求
    class ProdRequestStrategy implements RequestStrategy {
      async request(url: string, data?: any): Promise<any> {
        // 模拟生产接口请求(拼接生产域名)
        const prodUrl = `https://api.com${url}`;
        await new Promise(resolve => setTimeout(resolve, 500));
        return {
          code: 200,
          msg: 'success',
          data: { env: 'prod', url: prodUrl, data }
        };
      }
    }
    
    // 3. 环境类:请求工具
    class RequestContext {
      private strategy: RequestStrategy;
    
      // 根据环境变量初始化策略
      constructor(env: 'dev' | 'test' | 'prod') {
        switch (env) {
          case 'dev':
            this.strategy = new DevRequestStrategy();
            break;
          case 'test':
            this.strategy = new TestRequestStrategy();
            break;
          case 'prod':
            this.strategy = new ProdRequestStrategy();
            break;
          default:
            this.strategy = new DevRequestStrategy();
        }
      }
    
      setStrategy(strategy: RequestStrategy) {
        this.strategy = strategy;
      }
    
      async doRequest(url: string, data?: any): Promise<any> {
        return this.strategy.request(url, data);
      }
    }
    
    // 测试
    (async () => {
      // 开发环境请求
      const devRequest = new RequestContext('dev');
      console.log(await devRequest.doRequest('/user', { id: 1 })); 
      // { code: 200, msg: 'success', data: { mock: true, url: '/user', data: { id: 1 } } }
    
      // 切换到生产环境
      devRequest.setStrategy(new ProdRequestStrategy());
      console.log(await devRequest.doRequest('/user', { id: 1 }));
      // { code: 200, msg: 'success', data: { env: 'prod', url: 'https://api.com/user', data: { id: 1 } } }
    })();
    

策略模式的优势

  1. 开闭原则:可以在不修改原有代码的情况下增加新的策略;
  2. 避免多重条件语句:消除大量的 if-elseswitch-case
  3. 提高代码复用性:策略可以被不同的上下文复用;
  4. 策略可以自由切换:运行时可以动态改变对象的行为;

适用场景

  1. 表单验证 (不同字段需要不同验证规则)
  2. 数据格式化 (日期、货币、JSON等不同格式)
  3. 动画效果 (不同的缓动函数)
  4. 排序算法 (根据数据特点选择不同排序方式)
  5. 支付方式 (不同的支付渠道处理)
  6. 日志记录 (不同的日志级别和处理方式)
  7. 路由守卫 (不同的权限验证逻辑)
打赏作者
您的打赏是我前进的动力
微信
支付宝
评论

你好👏🏻,我是 ✍🏻   疯狂 codding 中...

粽子

这有关于前端开发的技术文档和你分享。

相信你可以在这里找到对你有用的知识和教程。

了解更多

目录

  1. 1. 策略模式核心概念
  2. 2. 策略模式的常用场景
    1. 2.1. 数据格式化
    2. 2.2. 支付方式选择
    3. 2.3. 动画 / 样式切换
    4. 2.4. 网络请求适配
  3. 3. 策略模式的优势
  4. 4. 适用场景