nicenote/docs/fea/pattern/action.md
2024-01-15 22:23:10 +08:00

7.8 KiB
Raw Blame History

nav group
title path
前端 /fea
title order path
💊 设计模式 6 /pattern

行为型

状态模式

状态模式

场景:

一台咖啡机,按需求给不同的咖啡:

  • 美式咖啡态american):只吐黑咖啡
  • 普通拿铁态(latte):黑咖啡加点奶
  • 香草拿铁态vanillaLatte黑咖啡加点奶再加香草糖浆
  • 摩卡咖啡态(mocha):黑咖啡加点奶再加点巧克力
import React, { useRef, useEffect } from 'react';

class CoffeeMachine {
  constructor() {
    this.state = 'init';
    this.leftMilk = '500ml';
  }

  // 美式
  american() {
    // 尝试在行为函数里拿到咖啡机实例的信息并输出
    console.log('咖啡机现在的牛奶存储量是:', this.leftMilk);
    console.log('我只吐黑咖啡');
    return '美式';
  }

  // 普通拿铁
  latte() {
    this.american();
    console.log('加点奶');
    return '普通拿铁';
  }

  // 香草拿铁
  vanillaLatte() {
    this.latte();
    console.log('再加香草糖浆');
    return '香草拿铁';
  }

  // 摩卡
  mocha() {
    this.latte();
    console.log('再加巧克力');
    return '摩卡';
  }

  changeState(state) {
    this.state = state;
    if (!this[state]) {
      return;
    }
    return this[state]();
  }
}

const mk = new CoffeeMachine();

export default () => <div>咖啡类型{mk.changeState('mocha')}</div>;

观察者模式(发布订阅者模式)

定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使它们能够自动更新自己,当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。

简单版

import React from 'react';
// 主题 保存状态,状态变化之后触发所有观察者对象
class Publisher {
  constructor() {
    this.state = 0;
    this.observers = [];
  }
  getState() {
    return this.state;
  }
  setState(state) {
    this.state = state;
    this.notifyAllObservers();
  }
  notifyAllObservers() {
    this.observers.forEach((observer) => {
      observer.update();
    });
  }
  attach(observer) {
    this.observers.push(observer);
  }
}

// 观察者
class Observer {
  constructor(name, publisher) {
    this.name = name;
    this.publisher = publisher;
    this.publisher.attach(this);
  }
  update() {
    console.log(`${this.name} 更新, state: ${this.publisher.getState()}`);
  }
}

// 测试
let pub = new Publisher();
let o1 = new Observer('o1', pub);
let o2 = new Observer('02', pub);

pub.setState(12);

export default () => null;

复杂版

// 定义发布者
class Publisher {
  constructor() {
    this.observers = []
    console.log('发布者创建-执行')
  }

  // 增加订阅者
  add(observer) {
    console.log('增加订阅者-执行')
    this.observers.push(observer)
  }

  // 移除订阅者
  remove(observer) {
    console.log('发布者移除-执行')
    this.observers.forEach((item, i) => {
      if (item === observer) {
        this.observers.splice(i, 1)
      }
    })
  }

  // 通知所有订阅者
  notify() {
    console.log('通知所有订阅者-执行')
    this.observers.forEach(observer => {
      observer.update(this)
    })
  }
}

// 定义订阅者类
class Observer {
  constructor() {
    console.log('订阅者创建-执行')
  }

  update() {
    console.log('订阅者被通知了!!')
  }
}

// 定义一个具体的需求文档发布类
class ProPublisher extends Publisher {
  constructor() {
    super()
    // 初始化需求文档
    this.proState = null
    this.observers = []
    console.log('业务订阅者创建-执行')
  }

  getState() {
    console.log('业务获取状态-执行')
  }

  setState(state) {
    console.log('业务设置状态-执行')
    this.proState = state
    this.notify()
  }
}

// 定义一个监听者
class ProObserver extends Observer {
  constructor() {
    super()
    // 需求文档一开始还不存在prd初始为空对象
    this.prdState = {}
    console.log('ProObserver created')
  }

  // 重写一个具体的update方法
  update(publisher) {
    console.log('ProObserver 已执行')
    // 更新需求文档
    this.prdState = publisher.getState()
    // 调用工作函数
    this.work()
  }

  // work方法一个专门搬砖的方法
  work() {
    // 获取需求文档
    const prd = this.prdState
    // 开始基于需求文档提供的信息搬砖。。。
    ...
    console.log('996 开始了...')
  }
}

// 创建订阅者前端开发C
const C = new ProObserver()
// 创建订阅者服务端开发小A
const A = new ProObserver()
// 创建订阅者测试同学小B
const B = new ProObserver()
// 韩梅梅出现了
const hanMeiMei = new PrdPublisher()
// 需求文档出现了
const prd = {
    // 具体的需求内容
    ...
}
// 韩梅梅开始拉群
hanMeiMei.add(C)
hanMeiMei.add(A)
hanMeiMei.add(B)
// 韩梅梅发送了需求文档,并@了所有人
hanMeiMei.setState(prd)

策略模式

和状态模式的区别,策略模式更偏向算法,计算逻辑的封装,状态更偏向定值的判断

简单版

业务场景如下:

马上大促要来了,我们本次大促要做差异化询价。啥是差异化询价?就是说同一个商品,我通过在后台给它设置不同的价格类型,可以让它展示不同的价格。具体的逻辑如下:

  • 当价格类型为“预售价”时,满 100 - 20不满 100 打 9 折
  • 当价格类型为“大促价”时,满 100 - 30不满 100 打 8 折
  • 当价格类型为“返场价”时,满 200 - 50不叠加
  • 当价格类型为“尝鲜价”时,直接打 5 折
import React, { useRef, useEffect } from 'react';

class CalculateMoney {
  // 处理预热价
  prePrice(originPrice) {
    if (originPrice >= 100) {
      return originPrice - 20;
    }
    return originPrice * 0.9;
  }

  // 处理大促价
  onSalePrice(originPrice) {
    if (originPrice >= 100) {
      return originPrice - 30;
    }
    return originPrice * 0.8;
  }

  // 处理返场价
  backPrice(originPrice) {
    if (originPrice >= 200) {
      return originPrice - 50;
    }
    return originPrice;
  }

  // 处理尝鲜价
  freshPrice(originPrice) {
    return originPrice * 0.5;
  }

  /**
   * type: pre 预售 | onSale 大促 | back 返场 | fresh 尝鲜
   * money: 原始金额
   **/
  static get(type, money) {
    switch (type) {
      case 'pre':
        return this.prototype.prePrice(money);
        break;
      case 'onSale':
        return this.prototype.onSalePrice(money);
        break;
      case 'back':
        return this.prototype.backPrice(money);
        break;
      case 'fresh':
        return this.prototype.freshPrice(money);
        break;
      default:
        return;
    }
  }
}

let calculateMoney = CalculateMoney.get('back', 10000);
// => 4000000

export default () => <div>初始金额10000策略模式{calculateMoney}</div>;

迭代器模式

少有的目的性极强的模式。

举例for...of...就是对 next 的反复调用。

// 自带迭代器语法
const arr = [1,2,3]
const iterator = arr[Symbol.iterator]()

iterator.next() -> { value: 1, done: false}
iterator.next() -> { value: 2, done: false}
iterator.next() -> { value: 3, done: false}
iterator.next() -> { value: undefined, done: true}

// 手撸版迭代器,闭包
const itaratorFunc = (arr: any  ) => {
  const idx = 0
  const len = arr.length

  if (Object.prototype.toString.call(arr) !== '[object Array]') throw new Error('请输入正确的数组格式')

  return {
    next: () => {
      var done = idx >= len
      var value = !done ? list[idx++] : undefined

      return {
        done,
        value
      }
    }
  }
}

const iterator = itaratorFunc([1,2,3])

const iterator.next()
const iterator.next()
const iterator.next()