修改设计模式

This commit is contained in:
haishan 2021-09-28 11:23:06 +08:00
parent f3f3c12566
commit 7014364e28
11 changed files with 595 additions and 331 deletions

64
docs/fea/pattern/about.md Normal file
View File

@ -0,0 +1,64 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 设计模式
order: 1
---
## 关于
## SOLID 设计原则
设计模式需要遵循五个原则被称为SOLID原则内容分别是
* 单一功能原则Single Responsibility Principle
* 开放封闭原则Opened Closed Principle
* 里式替换原则Liskov Substitution Principle
* 接口隔离原则Interface Segregation Principle
* 依赖反转原则Dependency Inversion Principle
> "SOLID" 是由罗伯特·C·马丁在 21 世纪早期引入的记忆术首字母缩略字,指代了面向对象编程和面向对象设计的五个基本原则。
## 设计模式分类早期22种新增1种
### 创建型
创建型模式封装了创建对象过程中的变化,它做的事情就是将创建对象的过程抽离;
* 单例模式
* 原型模式
* 构造器模式
* 工厂模式
* 抽象工厂模式
* 建造者(新)
### 结构型
结构型模式封装的是对象之间组合方式的变化,目的在于灵活地表达对象间的配合与依赖关系;
* 桥接模式
* 外观模式
* 组合模式
* 装饰器模式
* 适配器模式
* 代理模式
* 享元模式
### 行为型
而行为型模式则将是对象千变万化的行为进行抽离,确保我们能够更安全、更方便地对行为进行更改
* 迭代器模式
* 解释器模式
* 观察者模式
* 中介者模式
* 访问者模式
* 状态模式
* 备忘录模式
* 策略模式
* 模板方法模式
* 职责链模式
* 命令模式

View File

@ -7,11 +7,13 @@ group:
order: 6
---
## 发布订阅者模式
## 行为型
### 发布订阅者模式
定义了一种一对多的关系,让多个观察者对象同时监听某一个主题对象,这个主题对象的状态发生变化时就会通知所有的观察者对象,使它们能够自动更新自己,当一个对象的改变需要同时改变其它对象,并且它不知道具体有多少对象需要改变的时候,就应该考虑使用观察者模式。
### 简单版
#### 简单版
```js
// 主题 保存状态,状态变化之后触发所有观察者对象
@ -57,7 +59,7 @@ let o2 = new Observer('02', s)
s.setState(12)
```
### 复杂版
#### 复杂版
```js
/**
@ -95,4 +97,37 @@ export default class EventEmitter {
}
}
}
```
```
### 策略模式
```js
class CalculateMoney {
constructor(type, money) {
this.money = money
return this.switchStrategy(type, money)
}
/**
*
* @param {string} type 类型
* @param {number} money 金额
*/
switchStrategy(type, money) {
let tp = type.toLocaleLowerCase()
if (tp === 'a') {
return this.money = money * 100
} else if (tp === 'b') {
return this.money = money * 200
} else if (tp === 'c') {
return this.money = money * 300
} else {
return this.money = money * 400
}
}
}
let calculateMoney = new CalculateMoney('', 10000)
// => 4000000
```

View File

@ -1,39 +0,0 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 设计模式
order: 4
---
## 适配器模式
将一个类的接口转化为另外一个接口,以满足用户需求,使类之间接口不兼容问题通过适配器得以解决。
```js
import react from 'react';
class Adapter {
constructor(config) {
if (!config || typeof config === 'object') {
return this.reLoad(config)
} else {
throw new Error('传递的参数必须为一个对象!')
}
}
reLoad(arr) {
let newObj = {}
for (let i = 0; i < arr.length; i++) {
const item = arr[i]
newObj[item.name] = item.age
}
return newObj
}
}
let aer = new Adapter([{name: 'david', age: 2}])
// => {david: 2}
```

View File

@ -1,51 +0,0 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 设计模式
order: 3
---
## 建造者模式
```js
// 建造者模式 需要优化
export default class Builder {
constructor() {
this.name = ''
this.author = ''
this.price = 0
this.category = ''
}
withName(name) {
this.name = name
return this
}
withAuthor(author) {
this.author = author
return this
}
withPrice(price) {
this.price = price
return this
}
withCategory(category) {
this.category = category
return this
}
build() {
return {
name: this.name,
author: this.author,
price: this.price,
category: this.category
}
}
}
```

View File

@ -0,0 +1,301 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 设计模式
order: 2
---
## 创建型
创建型模式封装了创建对象过程中的变化,它做的事情就是将创建对象的过程抽离;
### 工厂模式
工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型。
#### 简单版
场景是我需要创建500个用户他们分别有不同的角色页面访问不同权限。
```js
class User {
constructor(name, type, viewPage) {
this.type = type
this.name = name
this.viewPage = viewPage
}
}
export default function getUserTypeFactory(name, type) {
let viewPage
switch (type) {
case 'superAdmin':
viewPage = ['首页', '通讯录', '发现页', '应用数据', '权限管理']
break;
case 'admin':
viewPage = ['首页', '通讯录', '发现页', '应用数据']
break;
case 'normal':
viewPage = ['首页', '通讯录', '发现页']
break;
default:
throw new Error('参数错误可选参数superAdmin、admin、normal')
}
return new User(name, type, viewPage)
}
```
### 抽象工厂模式
结合上文中的工厂,发现它不完全遵循 **SOLID法则** 中的开放封闭原则:对拓展开放,对修改封闭。
这篇文章我用一个手机从出产到上市使用的一个创建过程,首先我们知道,手机由两部分组成 **软件** **硬件**
```js
// 1-1. 手机初始化(抽象工厂):只定义
class AbstractFactory {
// 操作系统接口
createOS() {
throw new Error('抽象方法不允许直接调用,你需要将我重写')
}
// 提供硬件接口
createHardWare() {
throw new Error('抽象方法不允许直接调用,你需要将我重写')
}
}
// 1-2. 具体工厂:负责干活
class FakeStarFactory extends AbstractFactory {
// 操作系统接口
createOS() {
// 安卓系统实例
return new AndroidOS()
}
// 提供硬件接口
createHardWare() {
// 高通硬件实例
return new QualcommHardWare()
}
}
// 2-1. 抽象产品(软件):只定义
class OS {
controlHardWare() {
throw new Error('抽象方法不允许直接调用,你需要将我重写')
}
}
// 2-2. 具体产品:安卓操作系统
class AndroidOS extends OS {
controlHardWare() {
console.log('我会用安卓的方法去运行操作系统')
}
}
// 2-2. 具体产品:苹果操作系统
class AppleOS extends OS {
controlHardWare() {
console.log('我会用苹果的方法去运行操作系统')
}
}
// 3-1. 抽象产品(硬件):只定义
class HardWare {
// 手机硬件的共性方法:根据命令运转
operateByOrder() {
throw new Error('抽象方法不允许直接调用,你需要将我重写')
}
}
// 3-2. 具体产品(硬件):高通
class QualcommHardWare extends HardWare {
operateByOrder() {
console.log('我会用高通的方式去运转')
}
}
// 3-2. 具体产品(硬件):英特尔
class InterHardWare extends HardWare {
operateByOrder() {
console.log('我会用英特尔的方式去运转')
}
}
// 4. 创建手机
const myMobile = new FakeStarFactory();
// 初始化操作系统
const myOS = myMobile.createOS();
// 初始化硬件
const myHardWare = myOS.createHardWare();
// 启动操作系统
myOS.controlHardWare()
// 唤醒硬件命令
myHardWare.operateByOrder()
```
以上就是我们通过场景创建的四个重要的角色:
* 抽象工厂(抽象类,它不能被用于生成具体实例): 用于声明最终目标产品的共性。在一个系统里,抽象工厂可以有多个(大家可以想象我们的手机厂后来被一个更大的厂收购了,这个厂里除了手机抽象类,还有平板、游戏机抽象类等等),每一个抽象工厂对应的这一类的产品,被称为“产品族”。
* 具体工厂(用于生成产品族里的一个具体的产品): 继承自抽象工厂、实现了抽象工厂里声明的那些方法,用于创建具体的产品的类。
* 抽象产品(抽象类,它不能被用于生成具体实例): 上面我们看到,具体工厂里实现的接口,会依赖一些类,这些类对应到各种各样的具体的细粒度产品(比如操作系统、硬件等),这些具体产品类的共性各自抽离,便对应到了各自的抽象产品类。
* 具体产品(用于生成产品族里的一个具体的产品所依赖的更细粒度的产品): 比如我们上文中具体的一种操作系统、或具体的一种硬件等。
总结下来,抽象工厂就类似 typescript 里面的 interface 申明对象。
### 单例模式
保证一个类仅有一个实例,并提供一个访问它的全局访问点,这样的模式就叫做单例模式。
#### 简单版
```js
// ------------------- 单例模式 -------------------
export default class Singleton {
show() {
console.log('我是一个单例对象')
}
static getInstance() {
if (!this.instance) {
this.instance = new Singleton()
}
return this.instance
}
}
const s1 = Singleton.getInstance()
const s2 = Singleton.getInstance()
s1 === s2 // => true
```
#### 进阶版
实现一个 Storage
```js
class Storage {
constructor() {
this.instance = null
}
static getInstance() {
if (!this.instance) {
this.instance = new Storage()
}
return this.instance
}
getItem(key) {
return localStorage.getItem(key)
}
setItem(key, value) {
return localStorage.setItem(key, value)
}
}
```
### 构造器模式
原型模式不仅是一种设计模式它还是一种编程范式programming paradigm是 JavaScript 面向对象系统实现的根基。
```js
// 一个构造函数
class Asshole {
constructor(name, age) {
this.name = name
this.age = age
}
say() {
console.log(`I'm asshole ${this.name},${this.age} years old.`)
}
}
const me = new Asshole('json', 28)
```
**谈原型模式,其实是谈原型范式**,原型编程范式的核心思想就是利用实例来描述对象,用实例作为定义对象和继承的基础。在 JavaScript 中,原型编程范式的体现就是基于原型链的继承。这其中,对 **原型、原型链** 的理解是关键。
而面试官向你发问也并非是要求你破解人类未解之谜,多数情况下,他只是希望考查你对递归的熟练程度。所以递归实现深拷贝的核心思路,大家需要重点掌握(解析在注释里):
> 深拷贝没有完美方案,每一种方案都有它的边界 case
```ts
const deepClone = (obj) => {
// 如果是值类型或者不存在,直接返回
if (typeof obj !== 'object' || obj === null) return obj
// 定义结果对象
let copy = {}
// 如果对象是数组,则定义结果数组
if (obj.constructor === Array) copy = []
// 遍历对象的 key
for (let key in obj) {
// 如果key是对象的自身属性
if (obj.hasOwnProperty(key)) {
// 递归调用深拷贝方法
copy[key] = deepClone(obj[key])
}
}
return copy
}
```
### 建造者模式
```js
// 建造者模式 需要优化
export default class Builder {
constructor() {
this.name = ''
this.author = ''
this.price = 0
this.category = ''
}
withName(name) {
this.name = name
return this
}
withAuthor(author) {
this.author = author
return this
}
withPrice(price) {
this.price = price
return this
}
withCategory(category) {
this.category = category
return this
}
build() {
return {
name: this.name,
author: this.author,
price: this.price,
category: this.category
}
}
}
```

View File

@ -1,40 +0,0 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 设计模式
order: 3
---
## 装饰器模式
- 动态地给某个对象添加一些额外的职责,,是一种实现继承的替代方案
- 在不改变原对象的基础上,通过对其进行包装扩展,使原有对象可以满足用户的更复杂需求,而不会影响从这个类中派生的其他对象
```js
class Cellphone {
create() {
console.log('生成一个手机')
}
}
class Decorator {
constructor(cellphone) {
this.cellphone = cellphone
}
create() {
this.cellphone.create()
this.createShell(cellphone)
}
createShell() {
console.log('生成手机壳')
}
}
// 测试代码
let cellphone = new Cellphone()
cellphone.create()
console.log('------------')
let dec = new Decorator(cellphone)
dec.create()
```

View File

@ -1,61 +0,0 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 设计模式
order: 22
---
## 工厂模式
工厂模式定义一个用于创建对象的接口,这个接口由子类决定实例化哪一个类。该模式使一个类的实例化延迟到了子类。而子类可以重写接口方法以便创建的时候指定自己的对象类型。
```js
class User {
constructor(type) {
if (new.target === User) {
throw new Error('抽象类不能实例化!')
}
this.type = type
}
}
class SuperAdmin extends User {
constructor(name) {
super('superAdmin')
this.name = name
this.viewPage = ['首页', '通讯录', '发现页', '应用数据', '权限管理']
}
}
class Admin extends User {
constructor(name) {
super('admin')
this.name = name
this.viewPage = ['首页', '通讯录', '发现页', '应用数据']
}
}
class Normal extends User {
constructor(name) {
super('normal')
this.name = name
this.viewPage = ['首页', '通讯录', '发现页']
}
}
export default function getUserTypeFactory(type) {
switch (type) {
case 'superAdmin':
return SuperAdmin
break;
case 'admin':
return Admin
break;
case 'normal':
return Normal
break;
default:
throw new Error('参数错误可选参数superAdmin、admin、normal')
}
}
```

View File

@ -1,65 +0,0 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 设计模式
order: 5
---
## 代理模式
是为一个对象提供一个代用品或占位符,以便控制对它的访问
### ES5 版
```js
/**
* 代理模式
*/
export default class ProxyPro {
constructor(args) {
return new Proxy(args, {
get: function (target, key, receiver) {
// console.log(target, key, receiver);
console.log(`获取 ${key}`)
return Reflect.get(target, key, receiver)
},
set: function (target, key, value, reciver) {
console.log(`设置 ${key} 为 ${value}`)
return Reflect.set(target, key, value, reciver)
}
})
}
}
```
### ES6 版
```js
let Flower = function() {}
let xiaoming = {
sendFlower: function(target) {
let flower = new Flower()
target.receiveFlower(flower)
}
}
let B = {
receiveFlower: function(flower) {
A.listenGoodMood(function() {
A.receiveFlower(flower)
})
}
}
let A = {
receiveFlower: function(flower) {
console.log('收到花'+ flower)
},
listenGoodMood: function(fn) {
setTimeout(function() {
fn()
}, 1000)
}
}
xiaoming.sendFlower(B)
```

View File

@ -1,30 +0,0 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 设计模式
order: 2
---
## 单例模式
一个类只有一个实例,并提供一个访问它的全局访问点。
```js
// ------------------- 单例模式 -------------------
export default class Singleton {
constructor(name, creator, products) {
this.name = name
this.creator = creator
this.products = products
}
static getInstance(name, creator, products) {
if (!this.instance) {
this.instance = new Singleton(name, creator, products)
}
return this.instance
}
}
```

View File

@ -1,41 +0,0 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 设计模式
order: 7
---
## 策略模式
```js
class CalculateMoney {
constructor(type, money) {
this.money = money
return this.switchStrategy(type, money)
}
/**
*
* @param {string} type 类型
* @param {number} money 金额
*/
switchStrategy(type, money) {
let tp = type.toLocaleLowerCase()
if (tp === 'a') {
return this.money = money * 100
} else if (tp === 'b') {
return this.money = money * 200
} else if (tp === 'c') {
return this.money = money * 300
} else {
return this.money = money * 400
}
}
}
let calculateMoney = new CalculateMoney('', 10000)
// => 4000000
```

View File

@ -0,0 +1,191 @@
---
nav:
title: 前端
path: /fea
group:
title: 💊 设计模式
order: 4
---
## 结构型
结构型模式封装的是对象之间组合方式的变化,目的在于灵活地表达对象间的配合与依赖关系;
### 适配器模式
将一个类的接口转化为另外一个接口,以满足用户需求,使类之间接口不兼容问题通过适配器得以解决。
```js
import react from 'react';
class Adapter {
constructor(config) {
if (!config || typeof config === 'object') {
return this.reLoad(config)
} else {
throw new Error('传递的参数必须为一个对象!')
}
}
reLoad(arr) {
let newObj = {}
for (let i = 0; i < arr.length; i++) {
const item = arr[i]
newObj[item.name] = item.age
}
return newObj
}
}
let aer = new Adapter([{name: 'david', age: 2}])
// => {david: 2}
```
### 装饰器模式
- 动态地给某个对象添加一些额外的职责,,是一种实现继承的替代方案
- 在不改变原对象的基础上,通过对其进行包装扩展,使原有对象可以满足用户的更复杂需求,而不会影响从这个类中派生的其他对象
- 遵循拓展不修改的 SOLID 原则
#### 简单版
```jsx
import React, { useRef, useState, useEffect } from 'react';
class Modal {
constructor(opt = {}) {
const { dom } = opt
this.dom = dom
}
show() {
this.dom.innerHTML = '卧槽';
this.dom.style.display = 'block'
this.dom.style.width = '200px'
this.dom.style.textAlign = 'center'
}
hide() {
this.dom.style.display = 'none';
}
}
class DecoratorModal {
constructor(_oldModal) {
this._oldModal = _oldModal
}
show() {
this._oldModal.show()
this._oldModal.dom.innerHTML = '添加背景+文字减淡+圆角'
this._oldModal.dom.style.color = '#aaa'
this._oldModal.dom.style.borderRadius = '5px'
}
hide() {
this._oldModal.hide()
}
}
export default () => {
const modalRef = useRef(null)
const [modal, setModal] = useState(null)
// 案例:原本有个按钮,新的需求要将按钮样式置灰,并且文案改为 快去登录
const openModal = () => {
modal.show()
}
const hideModal = () => {
modal.hide()
}
const decoratorModal = () => {
let dom = new DecoratorModal(modal)
setModal(dom)
}
const normalModal = () => {
let dom = new Modal({
dom: modalRef.current
})
setModal(dom)
}
useEffect(() => {
normalModal()
}, [])
return (
<div className="decorator">
<button onClick={openModal} >打开弹框</button>
<button onClick={hideModal} >关闭弹框</button>
<button onClick={decoratorModal} >添加适配器</button>
<button onClick={normalModal} >清除适配器</button>
<div ref={modalRef} style={{ display: 'none', marginTop: '20px', padding: '10px 20px', border: '1px solid #eee'}} ></div>
</div>
)
};
```
## 代理模式
是为一个对象提供一个代用品或占位符,以便控制对它的访问
### ES5 版
```js
/**
* 代理模式
*/
export default class ProxyPro {
constructor(args) {
return new Proxy(args, {
get: function (target, key, receiver) {
// console.log(target, key, receiver);
console.log(`获取 ${key}`)
return Reflect.get(target, key, receiver)
},
set: function (target, key, value, reciver) {
console.log(`设置 ${key} 为 ${value}`)
return Reflect.set(target, key, value, reciver)
}
})
}
}
```
#### ES5 版
```js
let Flower = function() {}
let xiaoming = {
sendFlower: function(target) {
let flower = new Flower()
target.receiveFlower(flower)
}
}
let B = {
receiveFlower: function(flower) {
A.listenGoodMood(function() {
A.receiveFlower(flower)
})
}
}
let A = {
receiveFlower: function(flower) {
console.log('收到花'+ flower)
},
listenGoodMood: function(fn) {
setTimeout(function() {
fn()
}, 1000)
}
}
xiaoming.sendFlower(B)
```