一谈截、概述
設(shè)計模式是一套被反復(fù)使用的筷屡、多數(shù)人知曉的、經(jīng)過分類編目的簸喂、代碼設(shè)計經(jīng)驗(yàn)的總結(jié)毙死。使用設(shè)計模式是為了重用代碼、讓代碼更容易被他人理解喻鳄、保證代碼可靠性扼倘。
二、原則
S – Single Responsibility Principle 單一職責(zé)原則
- 一個程序只做好一件事
- 如果功能過于復(fù)雜就拆分開除呵,每個部分保持獨(dú)立
O – OpenClosed Principle 開放/封閉原則
- 對擴(kuò)展開放唉锌,對修改封閉
- 增加需求時,擴(kuò)展新代碼竿奏,而非修改已有代碼
L – Liskov Substitution Principle 里氏替換原則
- 子類能覆蓋父類
- 父類能出現(xiàn)的地方子類就能出現(xiàn)
I – Interface Segregation Principle 接口隔離原則
- 保持接口的單一獨(dú)立
- 類似單一職責(zé)原則,這里更關(guān)注接口
D – Dependency Inversion Principle 依賴倒轉(zhuǎn)原則
- 面向接口編程腥放,依賴于抽象而不依賴于具體
- 使用方只關(guān)注接口而不關(guān)注具體類的實(shí)現(xiàn)
三泛啸、分類
- 創(chuàng)建型
- 單例模式
- 原型模式
- 工廠模式
- 抽象工廠模式
- 建造者模式
- 結(jié)構(gòu)型
- 適配器模式
- 裝飾器模式
- 代理模式
- 外觀模式
- 橋接模式
- 組合模式
- 享元模式
- 行為型
- 觀察者模式
- 迭代器模式
- 策略模式
- 模板方法模式
- 職責(zé)鏈模式
- 命令模式
- 備忘錄模式
- 狀態(tài)模式
- 訪問者模式
- 中介模式
- 解釋器模式
四、示例
4.1單例模式
概述:
一個類只有一個實(shí)例(生成出來對象永遠(yuǎn)只有一個實(shí)例)秃症,并提供一個訪問它的全局訪問點(diǎn)候址。
實(shí)現(xiàn)過程:
- 構(gòu)建一個函數(shù)返回一個對象實(shí)例
- 用一個聲明一次變量來控制這個對象實(shí)例的生成吕粹。
- 如果該變量里已有一個對象,則直接返回岗仑,如果沒有匹耕,則生成,生成給變量存起來荠雕。
應(yīng)用:登錄框
閉包實(shí)現(xiàn)
function single(){
let obj //標(biāo)識
return function(){
if(!obj){ //判斷是否為undefined
obj = new Object()
}
return obj
}
}
let singleObj = single()
let obj1 = singleObj()
let obj2 = singleObj()
console.log(obj1===obj2)//true
原型實(shí)現(xiàn)
function singlePrototype(){
if(!Object.prototype.instance){
Object.prototype.instance = new Object()
}
return Object.prototype.instance
}
let obj1 = singlePrototype()
let obj2 = singlePrototype()
console.log(obj1===obj2) //true
static實(shí)現(xiàn)
function singleStatic(){
if(!Object.instance){
Object.instance = new Object()
}
return Object.instance
}
let obj1 = singleStatic()
let obj2 = singleStatic()
console.log(obj1===obj2)
全局變量實(shí)現(xiàn)
function singleWindow(){
if(!window.instance){
window.instance = new Object()
}
return window.instance
}
let obj1 = singleWindow()
let obj2 = singleWindow()
console.log(obj1===obj2)
4.2工廠模式
概述:
工廠模式生產(chǎn)對象的稳其,以一個工廠方法來生產(chǎn)對應(yīng)的對象。
實(shí)現(xiàn)過程:
- 手動構(gòu)建對象
- 手動給對象設(shè)置屬性
- 手動返回對象
function factory(){
let obj = new Object()
obj.name = 'jack'
return obj
}
4.3組合模式
概述:
將對應(yīng)多個相同名字方法 放在一個地方統(tǒng)一調(diào)用炸卑。
實(shí)現(xiàn):
class SayHello{
constructor(){
}
say(){
console.log('hello')
}
}
class SayHi{
constructor(){
}
say(){
console.log('hi')
}
}
class SayBay{
constructor(){
}
say(){
console.log('baybay')
}
}
以上的三個類 分別都具備一個名為say的方法 如果需要調(diào)用的話 那么是一個個的對象進(jìn)行調(diào)用而不能統(tǒng)一調(diào)用既鞠,如果我需要他統(tǒng)一調(diào)用,這個時候我們就可以使用組合模式盖文。
class Combiner{
constructor(){
//容器來保存對應(yīng)的對象
this.objs = []
}
push(obj){
//添加對象
this.objs.push(obj)
}
excute(fnName){
//執(zhí)行對應(yīng)的方法
this.objs.forEach(item=>{
item[fnName]()
})
}
}
//新建組合模式對象
let combiner = new Combiner()
//傳入對應(yīng)統(tǒng)一調(diào)用的對象
combiner.push(new SayHello())
combiner.push(new SayHi())
combiner.push(new SayBay())
//執(zhí)行對應(yīng)的方法
combiner.excute('say')
組合模式在vue中使用
use和install
vue.use()為注冊全局插件所用嘱蛋,接收函數(shù)或者一個包含install屬性的對象為參數(shù),如果參數(shù)帶有install就執(zhí)行install, 如果沒有就直接將參數(shù)當(dāng)install執(zhí)行, 第一個參數(shù)始終為vue對象, 注冊過的插件不會重新注冊
4.4觀察者模式
概述:
- 觀察者模式(obServer)他又被稱為發(fā)布-訂閱者模式五续,消息模式等洒敏。
- 定義了一種一對多的關(guān)系,讓多個觀察者對象同時監(jiān)聽某一個主題對象疙驾,這個主題對象的狀態(tài)發(fā)生變化時就會通知所有的觀察者對象凶伙,使它們能夠自動更新自己,當(dāng)一個對象的改變需要同時改變其它對象荆萤,并且它不知道具體有多少對象需要改變的時候镊靴,就應(yīng)該考慮使用觀察者模式。
場景:
- DOM事件
document.body.addEventListener('click', function() {
console.log('hello world!');
});
document.body.click()
- vue響應(yīng)式
實(shí)現(xiàn):
class ObServer{
constructor(){
//事件和對應(yīng)的處理函數(shù)存儲的容器
this.arg = {} //click:[fn,fn1]
}
on(){//發(fā)布事件
}
emit(){//執(zhí)行處理函數(shù)
}
off(){//取消事件
}
}
on方法實(shí)現(xiàn)
class ObServer{
constructor(){
this.arg = {} //{click:[fn,fn1]}
}
on(eventName, handler) { //發(fā)布事件 事件名 處理函數(shù)
if (!this.arg[eventName]) { //沒有這個事件
this.arg[eventName] = [] //初始化里面為空數(shù)組
}
this.arg[eventName].push(handler) //將對應(yīng)的函數(shù)追加
}
emit(){//執(zhí)行處理函數(shù)
}
off(){//取消事件
}
}
emit方法實(shí)現(xiàn)
class ObServer{
constructor(){
this.arg = {} //{click:[fn,fn1]}
}
on(eventName, handler) { //發(fā)布事件 事件名 處理函數(shù)
if (!this.arg[eventName]) { //沒有這個事件
this.arg[eventName] = [] //初始化里面為空數(shù)組
}
this.arg[eventName].push(handler) //將對應(yīng)的函數(shù)追加
}
emit(eventName, params) { //執(zhí)行處理函數(shù)
if (!this.arg[eventName]){
return
}
//會將里面的處理函數(shù)都執(zhí)行
//遍歷對應(yīng)的處理函數(shù)數(shù)組
this.arg[eventName].forEach(fn => {
//將參數(shù)傳入執(zhí)行
fn.call(this, params)
})
}
off(){//取消事件
}
}
off方法實(shí)現(xiàn)
class ObServer {
constructor() {
this.arg = {} //{click:[fn,fn1]}
}
on(eventName, handler) { //發(fā)布事件 事件名 處理函數(shù)
if (!this.arg[eventName]) { //沒有這個事件
this.arg[eventName] = [] //初始化里面為空數(shù)組
}
this.arg[eventName].push(handler) //將對應(yīng)的函數(shù)追加
}
emit(eventName, params) { //執(zhí)行處理函數(shù)
if (!this.arg[eventName]){
return
}
//會將里面的處理函數(shù)都執(zhí)行
//遍歷對應(yīng)的處理函數(shù)數(shù)組
this.arg[eventName].forEach(fn => {
//將參數(shù)傳入執(zhí)行
fn.call(this, params)
})
}
off(eventName, handler) { //取消事件
if (!this.arg[eventName]) {
return
}
//將這個對應(yīng)的fn刪除
if (this.arg[eventName].length == 1) {
delete this.arg[eventName]
} else {
let i
this.arg[eventName].forEach((item, index) => {
if (Object.is(item, handler)) {
i = index
}
})
this.arg[eventName].splice(i, 1)
}
}
}
擴(kuò)展:在觀察者emit方法傳入?yún)?shù) 傳到對應(yīng)的on里面的處理函數(shù) vue里面子傳父的實(shí)現(xiàn)
4.5代理模式
概述:
代理模式利用一個代理對象來處理當(dāng)前對象事情
假設(shè)當(dāng)A 在心情好的時候收到花链韭,小明表白成功的幾率有60%偏竟,而當(dāng)A 在心情差的時候收到花,小明表白的成功率無限趨近于0敞峭。小明跟A 剛剛認(rèn)識兩天踊谋,還無法辨別A 什么時候心情好。如果不合時宜地把花送給A旋讹,花被直接扔掉的可能性很大殖蚕,這束花可是小明吃了7 天泡面換來的。但是A 的朋友B 卻很了解A沉迹,所以小明只管把花交給B睦疫,B 會監(jiān)聽A 的心情變化,然后選擇A 心情好的時候把花轉(zhuǎn)交給A鞭呕,
es7新增一個類 Proxy 他就是用于代理的蛤育,他是vue3的底層實(shí)現(xiàn)
Proxy構(gòu)造函數(shù)
new Proxy(目標(biāo)對象,handler處理對象)
對應(yīng)的處理對象有4大方法
- get屬性 獲取對應(yīng)的代理對象的值調(diào)用
- set屬性 設(shè)置代理對象的值調(diào)用
- deleteProperty 刪除代理對象的屬性調(diào)用
- has屬性 在遍歷的時候調(diào)用
//目標(biāo)對象
let target = {name:'張三',age:18,say(){
console.log('hello');
}}
//利用proxy產(chǎn)生代理對象
let proxy = new Proxy(target,{
get(target,property,proxy){ //表示目標(biāo)對象 表示屬性名 表示代理對象
console.log('get調(diào)用了');
//訪問值的時候
if(property =='name'){
return '我的名字是'+target[property]
}
if(property =='age'){
return '我的年紀(jì)是'+target[property]+'歲'
}
},
set(target,property,value){
//設(shè)置值的時候 進(jìn)行相關(guān)操作
console.log(property);
console.log(value);
target[property] = value
},
deleteProperty(target,property,proxy){
//刪除屬性的時候
console.log('delete調(diào)用了');
delete target[property]
},
has(target,property){
//in的時候調(diào)用 必須返回boolean 強(qiáng)制轉(zhuǎn)換為boolean類型
console.log('has調(diào)用了');
console.log(property);
return property in target
},
apply(target,property){ //函數(shù)調(diào)用觸發(fā)
console.log('apply調(diào)用了');
}
})
//讀取代理對象的屬性的時候 會自動調(diào)用get方法 他的值是get方法返回的值
console.log(proxy.age); //調(diào)用get
proxy.name = 'jack' //調(diào)用set
console.log(proxy);
delete proxy.name //調(diào)用deleteProperty
console.log('name' in proxy); //某個東西是否在某個東西里面返回boolean
console.log(proxy.say); //代理只第一層
apply對應(yīng)的方法
function sum(a, b) {
return a + b;
}
const handler = {
apply: function (target, thisArg, argumentsList) { //目標(biāo)對象 當(dāng)前this 參數(shù)數(shù)組
console.log('apply調(diào)用了');
// expected output: "Calculate sum: 1,2"
return target(argumentsList[0], argumentsList[1]) * 10;
}
};
const proxy1 = new Proxy(sum, handler);
console.log(sum(1, 2));
// expected output: 3
console.log(proxy1(1, 2));
// expected output: 30
4.6裝飾者模式
概述:
- 動態(tài)地給某個對象添加一些額外的職責(zé),,是一種實(shí)現(xiàn)繼承的替代方案
- 在不改變原對象的基礎(chǔ)上瓦糕,通過對其進(jìn)行包裝擴(kuò)展底洗,使原有對象可以滿足用戶的更復(fù)雜需求,而不會影響從這個類中派生的其他對象
實(shí)現(xiàn):
//原本類
class Car{
constructor(){
}
run(){
console.log('車在跑')
}
}
//增強(qiáng)的類
class Decorater{
constructor(car){
this.car = car
}
run(){
console.log('我邊吃飯邊開車')
this.car.run()
}
}
new Decorater(new Car()).run()
擴(kuò)展:es7新增一個裝飾器 其實(shí)就是裝飾器模式的封裝
4.7適配器模式
概述:
將一個類的接口轉(zhuǎn)化為另外一個接口咕娄,以滿足用戶需求亥揖,使類之間接口不兼容問題通過適配器得以解決。
實(shí)現(xiàn):
let phone = {
fn(){
retrun '5v'
}
}
class Target{
constructor(){
}
fn(){
let v = phone.fn()
return '220轉(zhuǎn)換為'+v
}
}
new Target().fn()
應(yīng)用:
- 整合第三方SDK
- 封裝舊接口