nicenote/docs/fea/pattern/action.md
2021-11-23 13:33:08 +08:00

366 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
nav:
title: 前端
path: /fea
group:
title: 💊 设计模式
order: 6
---
## 行为型
### 状态模式
#### 状态模式
场景:
一台咖啡机,按需求给不同的咖啡:
- 美式咖啡态american):只吐黑咖啡
- 普通拿铁态(latte):黑咖啡加点奶
- 香草拿铁态vanillaLatte黑咖啡加点奶再加香草糖浆
- 摩卡咖啡态(mocha):黑咖啡加点奶再加点巧克力
```js
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>;
```
### 观察者模式(发布订阅者模式)
定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使它们能够自动更新自己,当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。
#### 简单版
```js
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;
```
#### 复杂版
```js
// 定义发布者
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 折
```jsx
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 的反复调用。
```js
// 自带迭代器语法
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()
```