導(dǎo)航
[封裝01-設(shè)計(jì)模式] 設(shè)計(jì)原則 和 工廠模式(簡(jiǎn)單抽象方法) 適配器模式 裝飾器模式
[封裝02-設(shè)計(jì)模式] 命令模式 享元模式 組合模式 代理模式
[React 從零實(shí)踐01-后臺(tái)] 代碼分割
[React 從零實(shí)踐02-后臺(tái)] 權(quán)限控制
[React 從零實(shí)踐03-后臺(tái)] 自定義hooks
[React 從零實(shí)踐04-后臺(tái)] docker-compose 部署react+egg+nginx+mysql
[React 從零實(shí)踐05-后臺(tái)] Gitlab-CI使用Docker自動(dòng)化部署
[源碼-webpack01-前置知識(shí)] AST抽象語(yǔ)法樹
[源碼-webpack02-前置知識(shí)] Tapable
[源碼-webpack03] 手寫webpack - compiler簡(jiǎn)單編譯流程
[源碼] Redux React-Redux01
[源碼] axios
[源碼] vuex
[源碼-vue01] data響應(yīng)式 和 初始化渲染
[源碼-vue02] computed 響應(yīng)式 - 初始化,訪問,更新過程
[源碼-vue03] watch 偵聽屬性 - 初始化和更新
[源碼-vue04] Vue.set 和 vm.$set
[源碼-vue05] Vue.extend
[源碼-vue06] Vue.nextTick 和 vm.$nextTick
[部署01] Nginx
[部署02] Docker 部署vue項(xiàng)目
[部署03] gitlab-CI
[數(shù)據(jù)結(jié)構(gòu)和算法01] 二分查找和排序
[深入01] 執(zhí)行上下文
[深入02] 原型鏈
[深入03] 繼承
[深入04] 事件循環(huán)
[深入05] 柯里化 偏函數(shù) 函數(shù)記憶
[深入06] 隱式轉(zhuǎn)換 和 運(yùn)算符
[深入07] 瀏覽器緩存機(jī)制(http緩存機(jī)制)
[深入08] 前端安全
[深入09] 深淺拷貝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模塊化
[深入13] 觀察者模式 發(fā)布訂閱模式 雙向數(shù)據(jù)綁定
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[深入19] 手寫Promise
[深入20] 手寫函數(shù)
[深入21] 數(shù)據(jù)結(jié)構(gòu)和算法 - 二分查找和排序
[深入22] js和v8垃圾回收機(jī)制
[深入23] JS設(shè)計(jì)模式 - 代理,策略,單例
[前端學(xué)java01-SpringBoot實(shí)戰(zhàn)] 環(huán)境配置和HelloWorld服務(wù)
[前端學(xué)java02-SpringBoot實(shí)戰(zhàn)] mybatis + mysql 實(shí)現(xiàn)歌曲增刪改查
[前端學(xué)java03-SpringBoot實(shí)戰(zhàn)] lombok,日志,部署
[前端學(xué)java04-SpringBoot實(shí)戰(zhàn)] 靜態(tài)資源 + 攔截器 + 前后端文件上傳
[前端學(xué)java05-SpringBoot實(shí)戰(zhàn)] 常用注解 + redis實(shí)現(xiàn)統(tǒng)計(jì)功能
[前端學(xué)java06-SpringBoot實(shí)戰(zhàn)] 注入 + Swagger2 3.0 + 單元測(cè)試JUnit5
[前端學(xué)java07-SpringBoot實(shí)戰(zhàn)] IOC掃描器 + 事務(wù) + Jackson
(一) 前置知識(shí)
(1)前置知識(shí)
scaffolding 腳手架
cluster 集群
handler 處理
Typography 排版
invoke 調(diào)用
maintainer 維護(hù)者
fly 蒼蠅
flyweight 享元模式
recover 回收
pattern 模式
design pattern 設(shè)計(jì)模式
strategy 策略模式
singleton 單列模式
expires 過期
(2)layout
xs => extra small 超小
sm => small
md => medium 中等的 中號(hào)的
lg => large
xl => extra large 超大
(3) pro-table
Protable => search={false}自定義查找
(4) 本地分支關(guān)聯(lián)遠(yuǎn)程分支
git branch --set-upstream-to=origin/remote_branch your_branch
(二) 命令模式
- 命令模式是一種高內(nèi)聚的模式
- 概念
- 將一個(gè)請(qǐng)求封裝成一個(gè)對(duì)象,從而讓你使用 ( 不同的請(qǐng)求將客戶端參數(shù)化 ),( 對(duì)請(qǐng)求排隊(duì)或者記錄日志 ),可以提供命令的撤銷或恢復(fù)功能
- 命令模式的核心在于引入了 ( 命令類 )
-
角色
調(diào)用者/發(fā)布者 invoker => 發(fā)布命令,調(diào)用命令對(duì)象,對(duì)reciver不可見,不知道誰(shuí)執(zhí)行和如何執(zhí)行
接收者/執(zhí)行者 receiver => 執(zhí)行命令,提供對(duì)應(yīng)接口來(lái)處理請(qǐng)求
命令對(duì)象 command => 命令對(duì)象,調(diào)用 ( 接收者對(duì)應(yīng)的接口 ) 來(lái)處理 ( 發(fā)布者的請(qǐng)求 )
-
特點(diǎn)
- invoker 和 reciver 相互獨(dú)立,將請(qǐng)求封裝成command,請(qǐng)求的具體執(zhí)行是 ( command對(duì)象調(diào)用reciver提供的接口來(lái)執(zhí)行 )
- ( 命令對(duì)象command ) 就是 ( 調(diào)用者invoker ) 和 ( 接收者reciver ) 的橋梁,解耦調(diào)用者和接收者
// 命令模式
// Invoker 發(fā)布者
// 1. 發(fā)布者中有 命令對(duì)象
// 2. 發(fā)布者發(fā)布命令,即調(diào)用命令對(duì)象中的方法,而命令對(duì)象又會(huì)去調(diào)用接收者的方法
class Invoker {
constructor(command) {
this.command = command
}
invoke = () => {
this.command.execute()
}
}
// Command 命令對(duì)象
// 1. 命令對(duì)象中有 接收者
// 2. 命令對(duì)象 調(diào)用接收者中的方法
class Command {
constructor(receiver) {
this.receiver = receiver
}
execute() {
this.receiver.execute()
}
}
// Receiver 接收者
// 接收者 執(zhí)行最終的請(qǐng)求
class Receiver {
execute() {
console.log('接收者執(zhí)行方法')
}
}
const cook = new Receiver() // 廚師-接收者-做飯
const shop = new Command(cook) // 商店-命令對(duì)象-命令廚師做飯
const user = new Invoker(shop) // 客戶-調(diào)用者-發(fā)布命令
user.invoke()
(三) 享元模式 flyweight pattern
- 利用共享技術(shù)來(lái)減少創(chuàng)建對(duì)象的數(shù)量,從而減少內(nèi)存占用,提高性能
- (
享元模式
) 提醒我們,將一個(gè) (對(duì)象的屬性
) 劃分為 (內(nèi)部狀態(tài)
) 和 (外部狀態(tài)
)- 內(nèi)部狀態(tài):被對(duì)象集合共享,通常不會(huì)改變
- 外部狀態(tài):根據(jù)應(yīng)用場(chǎng)景經(jīng)常改變
- 享元模式是 ( 用時(shí)間換空間 )
-
應(yīng)用場(chǎng)景
- 只要是需要大量創(chuàng)建 ( 重復(fù)的類的代碼 ),均可以使用享元模式抽離 ( 內(nèi)部狀態(tài)和外部狀態(tài) ),從而減少重復(fù)類的創(chuàng)建
- 相關(guān)單詞
- flyweight
- fly是蒼蠅的意思
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="radio" value="red" name="color" checked> 紅色
<input type="radio" value="yellow" name="color"> 黃色
<input type="radio" value="blue" name="color"> 藍(lán)色
<button onclick={draw()}>繪制</button>
<div id="container">顏色</div>
<script>
// 享元模式
class FlyweightDiv {
constructor() {
this.element = document.createElement('div') // ----- 內(nèi)部屬性,不變
}
setColor = (color) => { // ---------------------------- 外部外部,變化
this.element.style=`width: 100px; height: 100px; background-color:${color}`
}
}
const myDiv = new FlyweightDiv()
const draw = () => {
const btns = Array.from(document.getElementsByName('color'))
const checkedBtn = btns.find(btn => btn.checked)
const color = checkedBtn?checkedBtn.value:'red'
console.log(`color`, color)
myDiv.setColor(color)
document.getElementById('container').appendChild(myDiv.element)
// 注意:這里每次都是同一個(gè)element,所以不會(huì)新添加
}
</script>
</body>
</html>
(四) 組合模式 compose pattern
- 將 (
對(duì)象組合成樹形結(jié)構(gòu)
),以表示 (部分-整體
) 的層次結(jié)構(gòu) - 客戶可以使用統(tǒng)一的方式,對(duì)待 (
組合對(duì)象
) 和 (葉子對(duì)象
) - 又稱整體-部分模式
- 關(guān)鍵詞
- 部分/整體
- 葉子對(duì)象/組合對(duì)象
-
特點(diǎn)
- 樹葉形結(jié)構(gòu),是部分/整體的層次結(jié)構(gòu)
- 一致操作性,( 樹對(duì)象和葉對(duì)象 ) 對(duì)外接口保持一致,即 ( 操作與數(shù)據(jù)結(jié)構(gòu)一致 )
- 自上而下的請(qǐng)求流行,從 ( 樹對(duì)象傳遞給葉對(duì)象 )
-
調(diào)用頂層對(duì)象,會(huì)自行遍歷其下的葉對(duì)象執(zhí)行
image.png
實(shí)現(xiàn)一個(gè)樹形文件結(jié)構(gòu)
- folder 文件夾
- file 文件
- 文件夾下可以創(chuàng)建文件,文件不能再創(chuàng)建文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
// 組合模式
// 1. Floder 樹對(duì)象
// 2. File 葉對(duì)象
// 3. Folder 和 File 接口統(tǒng)一,都具有 add 和 scan 方法
class Floder {
constructor(name) {
this.name = name;
this.files = [];
}
add = (file) => {
this.files.push(file);
};
scan = () => {
this.files.forEach((file) => file.scan());
};
}
class File {
constructor(name) {
this.name = name
}
add = () => {
throw new Error('文件下面不能添加文件,文能在文件夾下添加文件')
}
scan = () => {
console.log(`正在掃描文件: ${this.name}`)
}
}
const carFolder = new Floder('車')
const bmwFile = new File('寶馬')
carFolder.add(bmwFile)
carFolder.scan()
</script>
</body>
</html>
(五) 復(fù)習(xí) - 代理模式 proxy pattern
- 定義
- 給 (
一個(gè)對(duì)象
) 提供一個(gè) (代理對(duì)象
),并由 (代理對(duì)象
) 來(lái)控制 (原對(duì)象的引用
),代理對(duì)象就類似于 (中介
)
- 給 (
- 角色
- 客戶對(duì)象
- 代理對(duì)象
- 委托對(duì)象
- ( 客戶對(duì)象 ) === ( 代理對(duì)象 ) === ( 委托對(duì)象 )
- 特點(diǎn)
- (
代理對(duì)象
) 本身不提供服務(wù),而是通過調(diào)用 (委托對(duì)象
) 的相關(guān)的方法來(lái)提供特定的服務(wù),即 (真正的業(yè)務(wù)功能還是由委托對(duì)象
) 來(lái)實(shí)現(xiàn) - ( 代理對(duì)象 ) 主要負(fù)責(zé)委托對(duì)象的 ( 預(yù)處理消息,過濾消息,轉(zhuǎn)發(fā)消息 )
- ( 代理類 ) 除了是 ( 客戶類 ) 和 ( 委托類 ) 的中介之外,還可以給代理類增加額外的功能來(lái)擴(kuò)展委托類的功能,這樣我們就可以直接修改代理類而不是直接修改委托類,符合代碼設(shè)計(jì)的開閉原則
- (
實(shí)戰(zhàn)
(5-1) 代理模式 - ajax請(qǐng)求添加緩存功能
- 原理
- ( 緩存 ) 每次請(qǐng)求的 ( 參數(shù) ) 和 ( 返回值 ),如果參數(shù)一樣,就直接返回 ( map ) 中參數(shù)對(duì)應(yīng)的返回值
利用 ( 代理模式 ) 給原有的函數(shù)添加新的邏輯但又不影響原函數(shù),相當(dāng)于用 ( 函數(shù)組合 ) 來(lái)實(shí)現(xiàn) ( 復(fù)用邏輯和添加邏輯 )
- 記得對(duì)比我之前的文章
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
// 代理模式
// 正常的請(qǐng)求
const request = (params) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
return resolve(`data: ${params}`);
}, 1000);
});
};
// 代理模式 - 代理請(qǐng)求
const proxyRequest = (params) => {
return cache(params);
};
// map 用來(lái)做參數(shù)和返回值的映射
const mapInstance = new Map();
// 利用map實(shí)現(xiàn)參數(shù)和返回值的緩存
const cache = async (params) => {
// 參數(shù)在map中存在,直接返回參數(shù)對(duì)應(yīng)的請(qǐng)求結(jié)果
if (mapInstance.has(params)) {
return mapInstance.get(params);
}
// 參數(shù)在map中不存在,則請(qǐng)求并做參數(shù)和請(qǐng)求返回值的映射
// 所以:當(dāng)參數(shù)存在時(shí),直接從map中返回,而不是重新請(qǐng)求
console.log("在本例中,我只會(huì)執(zhí)行一次");
const res = await request(params);
mapInstance.set(params, res);
return res;
};
proxyRequest("參數(shù)").then((res) => console.log(`res`, res));
setTimeout(() => {
proxyRequest("參數(shù)").then((res) => console.log(`res`, res));
}, 2000);
</script>
</body>
</html>
(5-2) 代理模式 - 代理緩存,處理緩存過期時(shí)間
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<script>
class Storage {
constructor(type, expires) {
this.storage = window[type];
this.expires = expires;
}
// 存
// 代理原生的 setItem() 方法,添加緩存過期時(shí)間
setItem = (key, value) => {
this.storage.setItem(
key,
JSON.stringify({
value,
setTime: +new Date(), // --------------------- ( 存 ) 的時(shí)間戳
})
);
};
// 取
// 代理原生的 getItem() 方法,根據(jù)傳入的過期時(shí)間,來(lái)判斷數(shù)據(jù)是否過期
getItem = (key) => {
const now = +new Date(); // --------------------- ( 取 ) 的時(shí)間戳
const { value, setTime } = JSON.parse(this.storage.getItem(key));
if (now - setTime > this.expires) {
this.storage.removeItem(key); // -------------- 過期刪除
}
return this.storage.getItem(key);
};
}
const localStorageInstance = new Storage("localStorage", 5000);
localStorageInstance.setItem("name", "woow_wu7");
console.log("未過期", localStorageInstance.getItem("name"));
setTimeout(() => {
console.log("過期", localStorageInstance.getItem("name"));
}, 6000);
</script>
</body>
</html>
(六) 復(fù)習(xí) - 策略模式 strategy pattern
- 概念
- 定義一系列算法,把他們一個(gè)個(gè)封裝起來(lái),并使他們可以 ( 相互替換 )
- 將 ( 不變的部分 ) 和 ( 變化的部 ) 分隔開,有點(diǎn)類似于 ( 享元模式 )
- (
策略模式
) 的主要目的就是將 (算法的使用
) 和 (算法的實(shí)現(xiàn)
) 分離開來(lái)
-
成員
- (
策略模式
) 主要包括 (策略類
) 和 (環(huán)境類
) -
策略類
- 封裝了具體的算法,并負(fù)責(zé)具體的計(jì)算過程
-
環(huán)境類
- ( 環(huán)境類 ) context負(fù)責(zé) ( 接受客戶的請(qǐng)求 ),隨后把 ( 請(qǐng)求委托給某一個(gè)策略類 )
- (
-
優(yōu)點(diǎn)
-
避免多重選擇語(yǔ)句出現(xiàn)
:策略模式利用 ( 組合 委托 多態(tài) ) 等技術(shù)和思想,可以有效的避免 ( 多重條件選擇語(yǔ)句,比如if...else或者switch的出現(xiàn) ) -
符合開放封閉的設(shè)計(jì)原則
:即可擴(kuò)展不可修改,將算法封裝在獨(dú)立的 strategy 策略中,使得他們?nèi)菀浊袚Q,理解,擴(kuò)展 -
算發(fā)可復(fù)用
:可以服用在系統(tǒng)其他地方,從而避免冗余重復(fù)的工作
-
-
缺點(diǎn)
- 必須了解所有的策略,必須了解各個(gè)策略的不同點(diǎn),才能實(shí)現(xiàn)一個(gè)特點(diǎn)的策略
實(shí)戰(zhàn)
(6-1) 計(jì)算獎(jiǎng)金
- 規(guī)則
- S績(jī)效 - 4倍工資
- A績(jī)效 - 3倍工資
- B績(jī)效 - 2倍工資
- 一些單詞
- bonus 獎(jiǎng)金
- salary 工資
- performance 績(jī)效 性能
- strategy pattern 策略模式
- 我之前的關(guān)于策略模式的文章
(1) 不做任何優(yōu)化的寫法
- 缺點(diǎn)
- 有很多if...else
- 缺乏擴(kuò)展性:如果要添加C績(jī)效,就得修改內(nèi)部的函數(shù)實(shí)現(xiàn),違反了開放封閉原則即可擴(kuò)展但 ( 不可修改 )
- 復(fù)用性差
- bonus 獎(jiǎng)金
- performance 績(jī)效
- salary 工資
const getBonus = (performance, salary) => {
if (performance === 'S') return 4 * salary;
if (performance === 'A') return 3 * salary;
if (performance === 'B') return 2 * salary;
}
getBonus('A', 1000) // 輸出 3000
-------
(2) 使用 策略模式 strategy pattern 重構(gòu)代碼
- 優(yōu)點(diǎn)
- 避免多個(gè)if...esle
- 符合開放/封閉原則,擴(kuò)展不需要修改原來(lái)的邏輯,也不會(huì)影響原來(lái)的邏輯
- 所有計(jì)算獎(jiǎng)金bonus的邏輯都不在getBonus函數(shù)即環(huán)境類context中,而是分布在各個(gè)策略對(duì)象strategyPattern中
- context環(huán)境類不負(fù)責(zé)計(jì)算,只是負(fù)責(zé)將請(qǐng)求委托給strategyPattern策略類
const strategyPattern = {
S: (salary) => 4 * salary,
A: (salary) => 3 * salary,
B: (salary) => 1 * salary,
}
const getBonus = (performance, salary) => strategyPattern[performance](salary)
getBonus('A', 1000) // 輸出3000
(6-2) 表單驗(yàn)證
- 不做任何優(yōu)化
(1) 不做任何優(yōu)化
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="javascript:void(0)" method="post">
<input type="text" name="username">
<input type="text" name="password">
<button>提交表單</button>
</form>
<script>
const form = document.querySelector('form')
form.onsubmit = (e) => {
if (form.username.value === '') {
console.log('用戶名不能為空')
return false
}
if (form.password.value.length < 6) {
console.log('密碼不能少于6位')
return false
}
console.log('提交成功')
return true
}
</script>
</body>
</html>
-
使用策略模式 - 優(yōu)化表單驗(yàn)證
- 第一步:將驗(yàn)證邏輯封裝到 ( 策略對(duì)象strategy中 )
- 第二步:將用戶的請(qǐng)求通過 ( 環(huán)境對(duì)象context ) 委托給 ( 策略對(duì)象strategy ) 來(lái)處理
- 我之前的關(guān)于策略模式的文章 - 表單驗(yàn)證 - 這里做了優(yōu)化
(2) 使用策略模式優(yōu)化 - 表單驗(yàn)證
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<form action="javascript:void(0)" method="post">
<input type="text" name="username" />
<input type="text" name="password" />
<button>提交表單</button>
</form>
<script>
const form = document.querySelector("form");
// strategy 策略對(duì)象
const strategy = {
notEmpty: (value, err) => { if (!value) return err },
minLength: (value, length, err) => { if (value.length < 6) return err}
}
// Validator 環(huán)境類
class Validator {
constructor() {
this.rules = [];
}
add = (value, rule, err) => { // --------------- add()方法的返回值是 err | undefined
const ruleArr = rule.split(":"); // ---------- [notEmpty] 或 [minLength, 6]
this.rules.push(() => { // ------------------- shift unshift push 改變?cè)瓟?shù)組
const strategyKey = ruleArr.shift(); // ---- 'notEmpty' 或 'minLength'
ruleArr.unshift(value) // ------------------ 頭部添加
ruleArr.push(err) // ----------------------- 尾部添加
// 1. ruleArr = [value, err] 或 [value, minLength, err]
// 2. 這里 apply 用得很秒,因?yàn)?ruleArr 是一個(gè)數(shù)組
return strategy[strategyKey].apply(null, ruleArr)
});
};
start = () => this.rules.map(rule => rule()) // start()返回一個(gè)err數(shù)組
}
form.onsubmit = (e) => {
e.preventDefault();
const validator = new Validator();
validator.add(form.username.value, "notEmpty", "用戶名不能為空"); // add()返回 err | undefined
validator.add(form.password.value, "minLength:6", "密碼不能少于6位"); // add()返回 err | undefined
const errArr = validator.start() // start()返回err數(shù)組,成員可能是undefined表示通過驗(yàn)證
const filterErrArr = errArr.filter(err => err) // 過濾掉通過驗(yàn)證的成員
filterErrArr.forEach(err => console.log(err)) // 打印錯(cuò)誤
if (filterErrArr.length>0) return false // 如果過濾掉通過驗(yàn)證成員后,數(shù)組長(zhǎng)度大于0,則表示有未通過驗(yàn)證的錯(cuò)誤,返回false
};
</script>
</body>
</html>
資料
命令模式(精簡(jiǎn)) https://juejin.cn/post/6844903889217519624
享元模式 http://techblog.sishuxuefu.com/atricle.html?5bcf34ef808ca40072a6fec2
享元模式 https://juejin.cn/post/6844903743901663246
組合模式 https://juejin.cn/post/6844903890375147527
代理模式[我的掘金] https://juejin.cn/post/6918744081460002824#heading-6