策略模式+状态机

0 阅读3分钟

prompt: 前端H5应用中,有一个每日签到提醒的投承场景,提醒的UI有3种形式,弹窗、小黄条提醒、toast,签到奖励又会有3种,但是最终只会汇总成一个提醒展示,请从数据驱动的角度,批量系统的处理以上功能,比如策略模式,或者更优秀的算法与数据结构处理方案

以下是基于数据驱动的每日签到提醒优先级管理方案,结合策略模式、优先级队列与状态机,实现高效且可扩展的提醒处理系统:


一、数据结构设计

1. 提醒规则配置表(JSON Schema)

// 优先级规则配置
const priorityRules = [
  {
    rewardType: 'vip',          // 奖励类型:vip/金币/道具
    conditions: {               // 触发条件
      isFirstLogin: true,       // 是否当日首次登录
      rewardValue: { min: 50 }  // 奖励值阈值
    },
    uiType: '弹窗',             // 对应UI类型
    priority: 1                 // 优先级(数值越小优先级越高)
  },
  {
    rewardType: '金币',
    conditions: { rewardValue: { min: 100 } },
    uiType: '小黄条',
    priority: 2
  },
  // ...其他规则
];

// 用户签到状态数据模型
class SignState {
  constructor() {
    this.rewards = [];          // 待领取奖励列表
    this.lastShownType = null;  // 上次展示的UI类型
    this.lastShownTime = 0;     // 上次展示时间戳
  }
}

二、核心算法与策略模式

1. 策略选择器(Strategy Selector)

class ReminderStrategy {
  // 策略接口
  getReminder(reward) {
    throw new Error('需实现策略方法');
  }
}

// 具体策略类
class PopupStrategy extends ReminderStrategy {
  getReminder(reward) {
    return { type: '弹窗', content: reward.detail };
  }
}

class ToastStrategy extends ReminderStrategy {
  getReminder(reward) {
    return { type: 'toast', content: reward.summary };
  }
}

// 策略工厂(根据规则动态选择)
class StrategyFactory {
  static createStrategy(rule) {
    switch (rule.uiType) {
      case '弹窗': return new PopupStrategy();
      case '小黄条': return new YellowBarStrategy();
      default: return new ToastStrategy();
    }
  }
}

2. 优先级队列处理

class PriorityQueue {
  constructor() {
    this.queue = [];
  }

  // 插入并排序(O(n)复杂度,适合小规模数据)
  enqueue(item) {
    let added = false;
    for (let i = 0; i < this.queue.length; i++) {
      if (item.priority < this.queue[i].priority) {
        this.queue.splice(i, 0, item);
        added = true;
        break;
      }
    }
    if (!added) this.queue.push(item);
  }

  dequeue() {
    return this.queue.shift();
  }
}

// 使用示例
const queue = new PriorityQueue();
rewards.forEach(reward => {
  const matchedRule = findMatchingRule(reward); // 匹配规则
  queue.enqueue({ 
    ...reward, 
    strategy: StrategyFactory.createStrategy(matchedRule),
    priority: matchedRule.priority
  });
});

三、状态驱动流程控制

1. 状态机管理(避免重复提醒)

class ReminderStateMachine {
  constructor() {
    this.state = new SignState();
  }

  // 状态转移判断
  shouldShow(reminderType) {
    const now = Date.now();
    const cooldown = this.getCooldown(reminderType);
    return now - this.state.lastShownTime > cooldown && 
           this.state.lastShownType !== reminderType;
  }

  getCooldown(type) {
    // 定义不同UI类型的冷却时间(ms)
    const cooldowns = { '弹窗': 3600000, '小黄条': 1800000, 'toast': 0 };
    return cooldowns[type] || 0;
  }
}

2. 批量处理流程

function processReminders(rewards) {
  const stateMachine = new ReminderStateMachine();
  const queue = new PriorityQueue();

  // 1. 规则匹配与入队
  rewards.forEach(reward => {
    const rule = priorityRules.find(r => 
      r.rewardType === reward.type && 
      checkConditions(r.conditions, reward)
    );
    if (rule) queue.enqueue({ reward, rule });
  });

  // 2. 优先级出队处理
  while (!queue.isEmpty()) {
    const { reward, rule } = queue.dequeue();
    const strategy = StrategyFactory.createStrategy(rule);
    
    if (stateMachine.shouldShow(rule.uiType)) {
      const reminder = strategy.getReminder(reward);
      showReminder(reminder);
      stateMachine.updateState(rule.uiType);
      break; // 只展示最高优先级
    }
  }
}

四、性能优化补充

  1. 规则预编译
    使用Web Worker预加载规则配置,将条件判断转换为JS函数:

    // 条件预编译示例
    function compileCondition(condition) {
      const code = Object.entries(condition)
        .map(([key, val]) => `reward.${key} >= ${val.min}`)
        .join(' && ');
      return new Function('reward', `return ${code}`);
    }
    
  2. 缓存策略
    对频繁触发的奖励类型增加缓存层,使用LRU算法管理:

    class ReminderCache {
      constructor(maxSize = 10) {
        this.cache = new Map();
        this.maxSize = maxSize;
      }
    
      getKey(reward) {
        return `${reward.type}_${reward.value}`;
      }
    
      get(reward) {
        const key = this.getKey(reward);
        if (this.cache.has(key)) {
          const item = this.cache.get(key);
          this.cache.delete(key);
          this.cache.set(key, item); // 刷新为最近使用
          return item;
        }
        return null;
      }
    }
    

五、扩展性设计

  1. 动态规则加载
    通过接口获取远程配置,支持热更新:

    async function loadRules() {
      const response = await fetch('/api/reminder-rules');
      priorityRules = await response.json();
      // 更新时重新初始化策略工厂
      StrategyFactory.init(priorityRules); 
    }
    
  2. AB测试支持
    在规则中增加实验分组字段,动态分配UI类型:

    {
      "rewardType": "金币",
      "experiment": {
        "groups": [
          { "name": "A组", "uiType": "弹窗", "weight": 0.2 },
          { "name": "B组", "uiType": "小黄条", "weight": 0.8 }
        ]
      }
    }
    

总结

该方案通过策略模式+优先级队列实现提醒决策,结合状态机控制展示频率,具备以下优势:

  1. 数据驱动:规则配置与业务逻辑解耦,支持动态调整
  2. 高效处理:优先级队列确保O(n)时间复杂度
  3. 可扩展性:新增UI类型只需添加策略类与规则配置
  4. 稳定性:状态机防止过度打扰用户

实际开发中可结合埋点监控(如记录规则匹配率、用户点击率)持续优化优先级策略。