366 lines
7.8 KiB
Markdown
366 lines
7.8 KiB
Markdown
---
|
||
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()
|
||
```
|