首頁 > 軟體

javascript的23種設計模式範例總結大全

2022-06-25 18:00:58

一、設計模式介紹

什麼是設計模式

  • 設計模式是解決問題的一種思想,和語言無關。在物件導向軟體設計的工程中,針對特定的問題簡潔優雅的一種解決方案。通俗一點的說,設計模式就是符合某種場景下某個問題的解決方案,通過設計模式可以增加程式碼的可重用性,可延伸性,可維護性,最終使得我們的程式碼高內聚、低耦合。

設計模式的五大設計原則

  • 單一職責:一個程式只需要做好一件事。如果功能過於複雜就拆分開,保證每個部分的獨立
  • 開放封閉原則:對擴充套件開放,對修改封閉。增加需求時,擴充套件新程式碼,而不是修改原始碼。這是軟體設計的終極目標。
  • 里氏置換原則:子類能覆蓋父類別,父類別能出現的地方子類也能出現。
  • 介面獨立原則:保持介面的單一獨立,避免出現“胖介面”。這點目前在TS中運用到。
  • 依賴導致原則:面向介面程式設計,依賴於抽象而不依賴於具體。使用方只專注介面而不用關注具體類的實現。俗稱“鴨子型別”

設計模式的三大類

  • 建立型:工廠模式,抽象工廠模式,建造者模式,單例模式,原型模式
  • 結構型:介面卡模式,裝飾器模式,代理模式,外觀模式,橋接模式,組合模式,享元模式
  • 行為型:策略模式,模板方法模式,釋出訂閱模式,迭代器模式,職責鏈模式,命令模式,備忘錄模式,狀態模式,存取者模式,中介者模式,直譯器模式。

二、設計模式

1.工廠模式

  • 工廠模式是用來建立物件的常見設計模式,在不暴露建立物件的具體邏輯,而是將邏輯進行封裝,那麼它就可以被稱為工廠。工廠模式又叫做靜態工廠模式,由一個工廠物件決定建立某一個類的範例。

優點

  • 呼叫者建立物件時只要知道其名稱即可
  • 擴充套件性高,如果要新增一個產品,直接擴充套件一個工廠類即可。
  • 隱藏產品的具體實現,只關心產品的介面。

缺點

  • 每次增加一個產品時,都需要增加一個具體類,這無形增加了系統記憶體的壓力和系統的複雜度,也增加了具體類的依賴

例子

  • 一個服裝廠可以生產不同型別的衣服,我們通過一個工廠方法類來模擬產出
class DownJacket {
  production(){
    console.log('生產羽絨服')
  }
}
class Underwear{
  production(){
    console.log('生產內衣')
  }
}
class TShirt{
  production(){
    console.log('生產t恤')
  }
}
// 工廠類
class clothingFactory {
  constructor(){
    this.downJacket = DownJacket
    this.underwear = Underwear
    this.t_shirt = TShirt
  }
  getFactory(clothingType){
    const _production = new this[clothingType]
    return _production.production()
  }
}
const clothing = new clothingFactory()
clothing.getFactory('t_shirt')// 生產t恤

2.抽象工廠模式

  • 抽象工廠模式就是通過類的抽象使得業務適用於一個產品類簇的建立,而不負責某一個類產品的範例。抽象工廠可以看作普通工廠的升級版,普通工廠以生產範例為主,而抽象工廠的目就是生產工廠。

優點

  • 當一個產品族中的多個物件被設計成一起工作時,它能保證使用者端始終只使用同一個產品族中的物件。

缺點

  • 產品族擴充套件非常困難,要增加一個系列的某一產品,既要在抽象的 Creator 里加程式碼,又要在具體的裡面加程式碼。

例子

  • 同樣基於上面的例子,模擬出一個抽象類,同時約束繼承子類的方法實現。最後再通過工廠函數返回指定的類簇
/* 抽象類
js中abstract是個保留字,實現抽象類只能通過new.target進行驗證,
防止抽象類被直接範例,另外如果子類沒有覆蓋指定方法,則丟擲錯誤
*/
class ProductionFlow {
  constructor(){
    if(new.target === ProductionFlow){
      throw new Error('抽象類不能被範例')
    }
  }
  production(){
    throw new Error('production要被重寫')
  }
  materials(){
    throw new Error('materials要被重寫')
  }
}
class DownJacket extends ProductionFlow{
  production(){
    console.log(`材料:${this.materials()},生產羽絨服`)
  }
  materials(){
    return '鴨毛'
  }
}
class Underwear extends ProductionFlow{
  production(){
    console.log(`材料:${this.materials()},生產內衣`)
  }
  materials(){
    return '絲光棉'
  }
}
class TShirt extends ProductionFlow{
  production(){
    console.log(`材料:${this.materials()},生產t恤`)
  }
  materials(){
    return '純棉'
  }
}
function getAbstractProductionFactory(clothingType){
  const clothingObj = {
    downJacket:DownJacket,
    underwear:Underwear,
    t_shirt:TShirt,
  }
  if(clothingObj[clothingType]){
    return clothingObj[clothingType]
  }
  throw new Error(`工廠暫時不支援生產這個${clothingType}型別的服裝`)
}
const downJacketClass = getAbstractProductionFactory('downJacket')
const underwearClass = getAbstractProductionFactory('underwear')
const downJacket = new downJacketClass()
const underwear = new underwearClass()
downJacket.production() // 材料:鴨毛,生產羽絨服
underwear.production() // 材料:絲光棉,生產內衣

3.建造者模式

  • 建造者模式是一種比較複雜使用頻率較低的建立型設計模式,建造者模式為使用者端返回的不是一個簡單的產品,而是一個由多個部件組成的複雜產品。主要用於將一個複雜物件的構建與他的表現分離,使得同樣的構建過程可以建立不同的表示。

優點

  • 建造者獨立易擴充套件
  • 方便控制細節風險

缺點

  • 產品必須有共同點,範圍有限制
  • 當內部有變化複雜時,會有很多建造類

例子

下面繼續用服裝廠的生產流程作為例子。

// 抽象類
class Clothing {
  constructor() {
    this.clothingType = ''
    this.price
  }
}
class Underwear extends Clothing {
  constructor() {
    super()
    this.clothingType = 'underwear'
    this.price = 10
  }
}
class TShirt extends Clothing {
  constructor() {
    super()
    this.clothingType = 't_shirt'
    this.price = 50
  }
}
class DownCoat extends Clothing {
  constructor() {
    super()
    this.clothingType = 'DownCoat'
    this.price = 500
  }
}
// 產品
class Purchase {
  constructor() {
    this.clothings = []
  }
  addClothing(clothing) {
    this.clothings.push(clothing)
  }
  countPrice() {
    return this.clothings.reduce((prev, cur)=>cur.price + prev,0)
  }
}
// 廠長
class FactoryManager {
  createUnderwear() {
    throw new Error(`子類必須重寫 createUnderwear`)
  }
  createTShirt() {
    throw new Error(`子類必須重寫 createTShirt`)
  }
  createDownCoat() {
    throw new Error(`子類必須重寫 DownCoat`)
  }
}
// 工人
class Worker extends FactoryManager {
  constructor() {
    super()
    this.purchase = new Purchase()
  }
  createUnderwear(num) {
    for (let i = 0; i < num; i++) {
      this.purchase.addClothing(new Underwear())
    }
  }
  createTShirt(num) {
    for (let i = 0; i < num; i++) {
      this.purchase.addClothing(new TShirt())
    }
  }
  createDownCoat(num) {
    for (let i = 0; i < num; i++) {
      this.purchase.addClothing(new DownCoat())
    }
  }
}
// 銷售
class Salesman {
  constructor() {
    this.worker = null
  }
  setWorker(worker) {
    this.worker = worker
  }
  reserve(clothing) {
    clothing.forEach((item) => {
      if (item.type === 'underwear') {
        this.worker.createUnderwear(item.num)
      } else if (item.type === 't_shirt') {
        this.worker.createTShirt(item.num)
      } else if (item.type === 'DownCoat') {
        this.worker.createDownCoat(item.num)
      } else {
        try {
          throw new Error('公司暫不生產或不存在該型別的商品')
        } catch (error) {
          console.log(error)
        }
      }
    });
    const purchase = this.worker.purchase
    return purchase.countPrice()
  }
}
const salesman = new Salesman()
const worker = new Worker()
salesman.setWorker(worker)
const order = [
  {
    type: 'underwear',
    num: 10
  },
  {
    type: 't_shirt',
    num: 4
  },
  {
    type: 'DownCoat',
    num: 1
  }
]
console.log(`本次訂單所需金額:${salesman.reserve(order)}`)

4.單例模式

  • 單例模式的思路是:保證一個類只能被範例一次,每次獲取的時候,如果該類已經建立過範例則直接返回該範例,否則建立一個範例儲存並返回。
  • 單例模式的核心就是建立一個唯一的物件,而在javascript中建立一個唯一的物件太簡單了,為了獲取一個物件而去建立一個類有點多此一舉。如const obj = {}obj就是獨一無二的一個物件,在全域性作用域的宣告下,可以在任何地方對它存取,這就滿足了單例模式的條件。

優點

  • 記憶體中只有一個範例,減少了記憶體的開銷。
  • 避免了對資源多重的佔用。

缺點

  • 違反了單一職責,一個類應該只關心內部邏輯,而不用去關心外部的實現

例子

  • 我們常見到的登入彈窗,要麼顯示要麼隱藏,不可能同時出現兩個彈窗,下面我們通過一個類來模擬彈窗。
class LoginFrame {
    static instance = null
    constructor(state){
        this.state = state
    }
    show(){
        if(this.state === 'show'){
            console.log('登入框已顯示')
            return
        }
        this.state = 'show'
        console.log('登入框展示成功')
    }
    hide(){
        if(this.state === 'hide'){
            console.log('登入框已隱藏')
            return
        }
        this.state = 'hide'
        console.log('登入框隱藏成功')
    }
    // 通過靜態方法獲取靜態屬性instance上是否存在範例,如果沒有建立一個並返回,反之直接返回已有的範例
    static getInstance(state){
        if(!this.instance){
            this.instance = new LoginFrame(state)
        }
        return this.instance
    }
}
const p1 = LoginFrame.getInstance('show')
const p2 = LoginFrame.getInstance('hide')
console.log(p1 === p2) // true

5.介面卡模式

  • 介面卡模式的目的是為了解決物件之間的介面不相容的問題,通過介面卡模式可以不更改原始碼的情況下,讓兩個原本不相容的物件在呼叫時正常工作。

優點

  • 讓任何兩個沒有關聯的類可以同時有效執行,並且提高了複用性、透明度、以及靈活性

缺點

  • 過多的使用介面卡模式,會讓系統變得零亂,不易整體把控。建議在無法重構的情況下使用介面卡。

例子

  • 拿一個現實中的例子來說,傑克只會英語,小明只會中文,它們在交流上出現了障礙,小紅同時會中英雙語,通過小紅將傑克的英語翻譯成中文,讓小明和傑克進行無障礙的溝通,這裡小紅就起到了介面卡的角色。
class Jack {
  english() {
    return 'I speak English'
  }
}
class Xiaoming {
  chinese() {
    return '我只會中文'
  }
}
// 介面卡
class XiaoHong {
  constructor(person) {
    this.person = person
  }
  chinese() {
    return `${this.person.english()} 翻譯: "我會說英語"`
  }
}
class Communication {
  speak(language) {
    console.log(language.chinese())
  }
}
const xiaoming = new Xiaoming()
const xiaoHong = new XiaoHong(new Jack())
const communication = new Communication()
communication.speak(xiaoming)
communication.speak(xiaoHong)

6.裝飾器模式

  • 裝飾者模式能夠在不更改原始碼自身的情況下,對其進行職責新增。相比於繼承裝飾器的做法更輕巧。通俗的講我們給心愛的手機上貼膜,帶手機殼,貼紙,這些就是對手機的裝飾。

優點

  • 裝飾類和被裝飾類它們之間可以相互獨立發展,不會相互耦合,裝飾器模式是繼承的一個替代模式,它可以動態的擴充套件一個實現類的功能。

缺點

  • 多層的裝飾會增加複雜度

例子

  • 在編寫飛機大戰的遊戲中,飛機物件的攻擊方式只有普通子彈攻擊,如何在不更改原始碼的情況下,為它其他的攻擊方式,如鐳射武器,導彈武器?
class Aircraft {
    ordinary(){
        console.log('發射普通子彈')
    }
}
class AircraftDecorator {
    constructor(aircraft){
        this.aircraft = aircraft
    }
    laser(){
        console.log('發射鐳射')
    }
    guidedMissile(){
        console.log('發射導彈')
    }
    ordinary(){
        this.aircraft.ordinary()
    }
}
const aircraft = new Aircraft()
const aircraftDecorator = new AircraftDecorator(aircraft)
aircraftDecorator.ordinary() // 發射普通子彈
aircraftDecorator.laser() // 發射鐳射
aircraftDecorator.guidedMissile() // 發射導彈
// 可以看到在不更改原始碼的情況下對它進行了裝飾擴充套件

7.代理模式

  • 代理模式的關鍵是,當客戶不方便直接存取一個物件或者不滿足需要的時候,提供一個替身物件來控制對這個物件的存取,客戶實際上存取的是替身物件。替身物件對請求做出一些處理之後,再把請求轉交給本體物件。
  • 代理和本體介面需要一致性,代理和本體之間可以說是鴨子型別的關係,不在乎他怎麼實現的,只要它們之間暴露的方法一致既可。

優點

  • 職責清晰,高擴充套件性,智慧化

缺點

  • 當物件和物件之間增加了代理可能會影響到處理的速度。
  • 實現代理需要額外的工作,有些代理會非常的複雜。

例子

  • 我們都知道,領導擁有公司的最高許可權,假設公司有員工100個,如果每個人都去找領導去處理事務,那領導肯定會崩潰,因此領導招聘了一個祕書幫他收集整理事務,祕書會在合適時間一次性將需要處理的業務交給老闆處理,在這裡祕書就是領導的一個代理角色。
// 員工
class Staff {
  constructor(affairType){
    this.affairType = affairType
  }
  applyFor(target){
    target.receiveApplyFor(this.affairType)
  }
}
// 祕書
class Secretary {
  constructor(){
    this.leader = new Leader()
  }
  receiveApplyFor(affair){
    this.leader.receiveApplyFor(affair)
  }
}
//領導
class Leader {
  receiveApplyFor(affair){
    console.log(`批准:${affair}`)
  }
}
const staff = new Staff('升職加薪')
staff.applyFor(new Secretary()) // 批准:升職加薪

8.外觀模式

  • 外觀模式本質就是封裝互動,隱藏系統的複雜性,提供一個可以存取的介面。由一個將子系統一組的介面整合在一起的高層介面,以提供一個一致的外觀,減少外界與多個子系統之間的直接互動,從而更方便的使用子系統。

優點

  • 減少系統的相互依賴,以及安全性和靈活性

缺點

  • 違反開放封閉原則,有變動的時候更改會非常麻煩,即使繼承重構都不可行。

例子

  • 外觀模式經常被用於處理高階遊覽器的和低版本遊覽器的一些介面的相容處理
function addEvent(el,type,fn){
    if(el.addEventlistener){// 高階遊覽器新增事件DOM API
        el.addEventlistener(type,fn,false)
    }else if(el.attachEvent){// 低版本遊覽器的新增事件API
        el.attachEvent(`on${type}`,fn)
    }else {//其他
        el[type] = fn
    }
}
  • 另一種場景,在某個函數中的某個引數可傳可不傳的情況下,通過函數過載的方式,讓傳參更靈活。
function bindEvent(el,type,selector,fn){
    if(!fn){
        fn = selector
    }
    // 其他程式碼
    console.log(el,type,fn)
}
bindEvent(document.body,'click','#root',()=>{})
bindEvent(document.body,'click',()=>{})

9.釋出訂閱模式

  • 釋出訂閱又稱觀察者模式,它定義物件之間的1對N的依賴關係,當其中一個物件發生變化時,所有依賴於它的物件都會得到通知。
  • 釋出訂閱模式經常出現在我們的工作場景中,如:當你給DOM繫結一個事件就已經使用了釋出訂閱模式,通過訂閱DOM上的click事件,當被點選時會向訂閱者釋出訊息。

優點

  • 觀察者和被觀察者它們之間是抽象耦合的。並且建立了觸發機制。

缺點

  • 當訂閱者比較多的時候,同時通知所有的訂閱者可能會造成效能問題。
  • 在訂閱者和訂閱目標之間如果迴圈參照執行,會導致崩潰。
  • 釋出訂閱模式沒有辦法提供給訂閱者所訂閱的目標它是怎麼變化的,僅僅只知道它變化了。

例子

  • 比喻前段時間的冬奧會,專案還沒有開始的時候可以提前預定,等到專案快開始的時,APP會提前給我們傳送通知即將開始的專案,而沒到時間的不通知,另外在專案還沒有開始的時候,可以取消訂閱避免接受到通知。根據這個需求我們來寫一個例子吧
class Subject {
  constructor(){
    this.observers = {}
    this.key = ''
  }
  add(observer){
    const key = observer.project
    if (!this.observers[key]) {
      this.observers[key] = []
    }
    this.observers[key].push(observer)
  }
  remove(observer){
    const _observers = this.observers[observer.project]
    console.log(_observers,11)
    if(_observers.length){
      _observers.forEach((item,index)=&gt;{
        if(item === observer){
          _observers.splice(index,1)
        }
      })
    }
  }
  setObserver(subject){
    this.key = subject
    this.notifyAllObservers()
  }
  notifyAllObservers(){
    this.observers[this.key].forEach((item,index)=&gt;{
      item.update()
    })
  }
}
class Observer {
  constructor(project,name) {
    this.project = project
    this.name = name
  }
  update() {
    console.log(`尊敬的:${this.name} 你預約的專案:【${this.project}】 馬上開始了`)
  }
}
const subject = new Subject()
const xiaoming = new Observer('滑雪','xiaoming')
const A = new Observer('大跳臺','A')
const B = new Observer('大跳臺','B')
const C = new Observer('大跳臺','C')
subject.add(xiaoming)
subject.add(A)
subject.add(B)
subject.add(C)
subject.remove(B) // 取消訂閱
subject.setObserver('大跳臺')
/** 執行結果
 * 尊敬的:A 你預約的專案:【大跳臺】 馬上開始了
 * 尊敬的:C 你預約的專案:【大跳臺】 馬上開始了
 */

10.迭代器模式

  • 迭代器模式是指提供一種方法順序存取一個聚合物件中的每個元素,並且不需要暴露該物件的內部。

優點

  • 它支援以不同的方式遍歷一個聚合物件。
  • 迭代器簡化了聚合類。在同一個聚合上可以有多個遍歷。
  • 在迭代器模式中,增加新的聚合類和迭代器類都很方便,無須修改原有程式碼。

缺點

  • 由於迭代器模式將儲存資料和遍歷資料的職責分離,增加新的聚合類需要對應增加新的迭代器類,類的個數成對增加,這在一定程度上增加了系統的複雜性。

例子

迭代器分為內部迭代器和外部迭代器,它們有各自的適用場景。

  • 內部迭代器
// 內部迭代器表示內部已經定義好了迭代規則,它完全接受整個迭代過程,外部只需一次初始呼叫。
Array.prototype.MyEach = function(fn){
    for(let i = 0;i<this.length;i++){
        fn(this[i],i,this)
    }
}
Array.prototype.MyEach = function(fn){
    for(let i = 0;i<this.length;i++){
        fn(this[i],i,this)
    }
}
[1,2,3,4].MyEach((item,index)=>{
    console.log(item,index)
})
  • 外部迭代器
// 外部迭代器必須顯示的迭代下一個元素。它增加了呼叫的複雜度,但也增加了迭代器的靈活性,可以手動控制迭代的過程。
class Iterator{
    constructor(arr){
        this.current = 0
        this.length = arr.length
        this.arr = arr
    }
    next(){
        return this.getCurrItem()
    }
    isDone(){
        return this.current>=this.length
    }
    getCurrItem(){
        return {
            done:this.isDone(),
            value:this.arr[this.current++]
        }
    }
}
let iterator =new Iterator([1,2,3])
while(!(item=iterator.next()).done) { 
    console.log(item) 
}
iterator.next()
/* 下面的資料格式是不是有點熟悉
{done: false, value: 1}
{done: false, value: 2}
{done: false, value: 3}
{done: true, value: undefined}
*/

11.狀態模式

  • 允許一個物件在其內部狀態改變的時候改變其行為,物件看起來似乎修改了它的類,通俗一點的將就是記錄一組狀態,每個狀態對應一個實現,實現的時候根據狀態去執行實現。

優點

  • 將所有與某個狀態有關的行為放到一個類中,並且可以方便地增加新的狀態,只需要改變物件狀態即可改變物件的行為。
  • 允許狀態轉換邏輯與狀態物件合成一體,而不是某一個巨大的條件語句塊。
  • 可以讓多個環境物件共用一個狀態物件,從而減少系統中物件的個數。

缺點

  • 狀態模式的使用必然會增加系統類和物件的個數。
  • 狀態模式的結構與實現都較為複雜,如果使用不當將導致程式結構和程式碼的混亂。
  • 狀態模式對"開閉原則"的支援並不太好,對切換狀態的狀態模式增加新的狀態類需要修改那些負責狀態轉換的原始碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需修改對應類的原始碼。

例子

  • lol中的瑞文的Q有三段攻擊,同一個按鍵,在不同的狀態下,攻擊的行為不同。通常情況下,我們通過if...else也可以實現,但是這樣明顯不利於擴充套件,違反了開放封閉原則。接下來用程式碼來描述這種場景。
class State {
  constructor(attack){
    this.attack = attack
  }
  handle(context){
    console.log(this.attack)
    context.setState(this)
  }
}
class Context {
  constructor(){
    this.state = null
  }
  getState(){
    return this.state
  }
  setState(state){
    this.state = state
  }
}
const q1 = new State('q1 第1擊'),
      q2 = new State('q2 第2擊'),
      q3 = new State('q3 第3擊'),
      context = new Context()
q1.handle(context)//q1 第1擊
q2.handle(context)//q2 第2擊
q3.handle(context)//q3 第3擊

12.策略模式

  • 策略模式指的是定義一系列演演算法,把他們一個個封裝起來,目的就是將演演算法的使用和演演算法的實現分離開來。同時它還可以用來封裝一系列的規則,比如常見的表單驗證規則,只要這些規則指向的目標一致,並且可以被替換使用,那麼就可以用策略模式來封裝它們。

優點

  • 演演算法可以自由切換,避免了使用多層條件判斷,增加了擴充套件性

缺點

  • 策略類增多,所有策略類都需要對外暴露。

例子

  • 剛入這個行業的時候,寫表單驗證經常無止境的if...else寫法,意識到這種寫法不靠譜,於是我把檢驗規則放在一個物件中,在函數中對它進行控制,把規則與實現進行了分離,每次只需要在封裝的規則中去修改設定。在後面的多種場景都用這種方法,解決了頻繁使用if...else的問題,當第一次接觸倒策略模式才知道這種寫法也算策略模式。
const rules = {
    cover_img: {
        must: false,
        msg: '請上傳封面圖片',
        val: ''
    },
    name: {
        must: true,
        msg: '姓名不能為空',
        val: ''
    },
    sex: {
        must: true,
        msg: '請填寫性別',
        val: ''
    },
    birthday: {
        must: false,
        msg: '請選擇生日',
        val: ''
    },
}
function verify(){
    for(const key in rules){
        if(rules[key].must&amp;&amp;!rules[key].val){
            console.log(rules[key].msg)
        }
    }
}
verify()
// 姓名不能為空
// 請填寫性別
  • 上面的例子是以js方式寫的,在javascript將函數作為一等公民的語言裡,策略模式就是隱形的,它已經融入到了javascript的語言中,所以以javascript方式的策略模式會顯得簡單直接。不過我們依然要了解傳統的策略模式,下面來看看傳統的策略模式的例子。
//html-----------------
<form action="http:// xxx.com/register" id="registerForm" method="post">
    請輸入使用者名稱:<input type="text" name="userName" />
    請輸入密碼:<input type="text" name="password" />
    請輸入手機號碼:<input type="text" name="phoneNumber" />
    <button>提交</button>
</form>
// js------------------
class Strategies {
  constructor() {
    this.rules = {}
  }
  add(key, rule) {
    this.rules[key] = rule
    return this
  }
}
class Validator {
  constructor(strategies) {
    this.cache = [] // 儲存檢驗規則
    this.strategies = strategies
  }
  add(dom, rules) {
    rules.forEach((rule) => {
      const strategyAry = rule.strategy.split(':')
      this.cache.push(() => {
        const strategy = strategyAry.shift()
        strategyAry.unshift(dom.value)
        strategyAry.push(rule.errorMsg)
        console.log(this.strategies[strategy])
        return this.strategies[strategy].apply(dom, strategyAry)
      })
    });
  }
  start() {
    for (let i = 0,validatorFunc; validatorFunc =this.cache[i++]; ) {
      const msg = validatorFunc()
      if (msg) {
        return msg
      }
    }
  }
}
const registerForm = document.getElementById('registerForm') // 獲取formDom節點
const strategies = new Strategies()
strategies.add('isNonEmpty', function(value, errorMsg) {
  if (!value) {
    return errorMsg
  }
}).add('minLength', function(value, length, errorMsg) {
  if (value.length < length) {
    return errorMsg
  }
}).add('isMobile', function(value, errorMsg) {
  if (!/(^1[3|5|8][0-9]{9}$)/.test(value)) {
    return errorMsg
  }
})
function validataFunc() {
  const validator = new Validator(strategies.rules)
  // 多個校驗規則
  validator.add(registerForm.userName, [
    {
      strategy: 'isNonEmpty',
      errorMsg: '使用者名稱不能為空'
    }, {
      strategy: 'minLength:10',
      errorMsg: '使用者名稱長度不能少於10位'
    }
  ])
  validator.add(registerForm.password, [{
    strategy: 'minLength:6',
    errorMsg: '密碼長度不能少於6位'
  }])
  validator.add(registerForm.phoneNumber, [{
    strategy: 'isMobile',
    errorMsg: '手機號碼格式不對'
  }])
  const errorMsg = validator.start()
  return errorMsg // 返回錯誤資訊。
}
registerForm.onsubmit = function () {
  const errorMsg = validataFunc()
  if (errorMsg) { // 如果存在錯誤資訊,顯示錯誤資訊,並且阻止onsubmit預設事件
    console.log(errorMsg)
    return false
  }
}

13.命令模式

  • 命令模式中的命令指的是一個執行某些特定的事情的指令。
  • 命令模式最常見的應用場景如:有時候需要向某些物件傳送請求,但是並不知道請求的接收者是誰,也不知道被請求的操作是什麼。此時可以通過一種鬆耦合的方式來設計程式,使得請求傳送者和請求接收者消除彼此之間的耦合關係。

優點

  • 降低了程式碼的耦合度,易擴充套件,出現新的命令可以很容易的新增進去

缺點

  • 命令模式使用過度會導致程式碼中存在過多的具體命令。

例子

  • 假設在一個專案中開發某個頁面,其中某個程式設計師負責繪製靜態頁面,包括某些按鈕,而另一個程式設計師負責開發這幾個按鈕的具體行為。負責靜態頁面的程式設計師暫時不知道這些按鈕未來會發生什麼,在不知道具體行為是什麼作什麼的情況下,通過命令模式的幫助,解開按鈕和負責具體行為物件之間的耦合。
// html-------------------
<button id="button2">點選按鈕 1</button>
<button id="button2">點選按鈕 2</button>
<button id="button3">點選按鈕 3</button>
// js---------------------
const button1 = document.getElementById('button1'),
  button2 = document.getElementById('button2'),
  button3 = document.getElementById('button3');
  const MenBar = {
    refresh:function(){
      console.log('重新整理選單目錄')
    }
  }
  const SubMenu = {
    add:function(){
      console.log('增加子選單')
    },
    del:function(){
      console.log('刪除子選單')
    }
  }
  function setCommand(el,command){
    el.onclick = function(){
      command.execute()
    }
  }
  class MenuBarCommand{
    constructor(receiver,key){
      this.receiver = receiver
      this.key = key
    }
    execute(){
      this.receiver[this.key]()
    }
  }
  setCommand(button1,new MenuBarCommand(MenBar,'refresh'))
  setCommand(button2,new MenuBarCommand(SubMenu,'add'))
  setCommand(button3,new MenuBarCommand(SubMenu,'del'))

14.組合模式

  • 組合模式就是由一些小的子物件構建出的更大的物件,而這些小的子物件本身可能也是由多個孫物件組合而成的。
  • 組合模式將物件組合成樹狀結構,以表示“部分-整體”的層次結構。除了用來表示樹狀結構之外,組合模式的另一個好處就是通過物件的多型性表現,使得使用者對單個物件和組合物件的使用具有一致性。

優點

  • 高層模組呼叫簡單,節點可以自由新增

缺點

  • 其葉物件和子物件宣告都是實現類,而不是介面,這違反了依賴倒置原則

例子

  • 以我們最常見的資料夾和檔案的關係,非常適合用組合模式來描述,資料夾可以包括子資料夾和檔案,檔案不能包括任何檔案,這種關係讓最終會形成一棵樹。下面來實現檔案的新增,掃描該檔案裡的檔案,並且可以刪除檔案。
// 資料夾類
class Folder {
  constructor(name) {
    this.name = name
    this.parent = null;
    this.files = []
  }
  // 新增檔案
  add(file) {
    file.parent = this
    this.files.push(file)
    return this
  }
  // 掃描檔案
  scan() {
    console.log(`開始掃描資料夾:${this.name}`)
    this.files.forEach(file =&gt; {
      file.scan()
    });
  }
  // 刪除指定檔案
  remove() {
    if (!this.parent) {
      return
    }
    for (let files = this.parent.files, i = files.length - 1; i &gt;= 0; i--) {
      const file = files[i]
      if (file === this) {
        files.splice(i, 1)
        break
      }
    }
  }
}
// 檔案類
class File {
  constructor(name) {
    this.name = name
    this.parent = null
  }
  add() {
    throw new Error('檔案下面不能新增任何檔案')
  }
  scan() {
    console.log(`開始掃描檔案:${this.name}`)
  }
  remove() {
    if (!this.parent) {
      return
    }
    for (let files = this.parent.files, i = files.length - 1; i &gt;= 0; i++) {
      const file = files[i]
      if (file === this) {
        files.splice(i, 1)
      }
    }
  }
}
const book = new Folder('電子書')
const js = new Folder('js')
const node = new Folder('node')
const vue = new Folder('vue')
const js_file1 = new File('javascript高階程式設計')
const js_file2 = new File('javascript忍者祕籍')
const node_file1 = new File('nodejs深入淺出')
const vue_file1 = new File('vue深入淺出')
const designMode = new File('javascript設計模式實戰')
js.add(js_file1).add(js_file2)
node.add(node_file1)
vue.add(vue_file1)
book.add(js).add(node).add(vue).add(designMode)
book.remove()
book.scan()

15.模組方法模式

  • 模組方法模式是一種基於繼承的設計模式,在javascript中沒有真正意義上的繼承,所有繼承都來自原型(prototype)上的繼承,隨著ES6的class到來,實現了繼承的“概念”,讓我們可以以一種很方便簡潔的方式繼承,但其本質上還是原型繼承。
  • 模板方法模式由兩部分組成,第一部分是抽象父類別,第二部分是具體的實現子類。抽象父類別主要封裝了子類的演演算法框架,以及實現了一些公共的方法和其他方法的執行順序。子類通過繼承父類別,繼承了父類別的演演算法框架,並進行重寫。

優點

  • 提供公共的程式碼便於維護。行為由父類別控制,具體由子類來實現。

缺點

  • 其每一個具體實現都需要繼承的子類來實現,這無疑導致類的個數增加,使得系統龐大。

例子

  • 拿咖啡和茶的例子來說,製作咖啡和茶都需要燒開水,把水煮沸是一個公共方法,隨後的怎麼沖泡,把什麼倒進杯子,以及新增什麼配料,它們可能各不一樣,根據以上特點,開始我們的例子。
// 抽象父類別
class Beverage {
  boilWater(){
    console.log('把水煮沸')
  }
  brew(){
    throw new Error('字類必須重寫brew方法')
  }
  pourInCup(){
    throw new Error('字類必須重寫pourInCup方法')
  }
  addCondiments(){
    throw new Error('字類必須重寫addCondiments方法')
  }
  init(){
    this.boilWater()
    this.brew()
    this.pourInCup()
    this.addCondiments()
  }
}
// 咖啡類
class Coffee extends Beverage {
  brew(){
    console.log('用沸水沖泡咖啡')
  }
  pourInCup(){
    console.log('把咖啡倒進杯子')
  }
  addCondiments(){
    console.log('加糖和牛奶')
  }
}
// 茶類
class Tea extends Beverage {
  brew(){
    console.log('用沸水侵泡茶葉')
  }
  pourInCup(){
    console.log('把茶倒進杯子')
  }
  addCondiments(){
    console.log('加檸檬')
  }
}
const coffee = new Coffee()
coffee.init()
const tea = new Tea()
tea.init()

16.享元模式

  • 享元模式是一種用於效能優化的模式,核心是運用共用技術來有效支援大量的細粒度物件。如果系統中建立了大量的類似物件,會導致記憶體消耗過高,通過享用模式處理重用類似物件,減少記憶體消耗的問題,達到效能優化方案。
  • 享元模式的關鍵是如何區分內部狀態和外部狀態
    • 內部狀態:可以被物件共用,通常不會改變的稱為內部狀態
    • 外部狀態:取決於具體的場景,根據具體的場景變化,並且不能被共用的稱為外部狀態

優點

  • 減少了大批次物件的建立,降低了系統了記憶體。

缺點

  • 提高了系統的複雜度,需要分離出外部狀態和內部狀態,而且外部狀態具有固有化的性質,不應該隨著內部狀態的變化而變化,否則會造成系統的混亂。

例子

let id = 0
// 定義內部狀態
class Upload {
  constructor(uploadType) {
    this.uploadType = uploadType
  }
  // 點選刪除時 小於3000直接刪除,大於3000通過confirm提示彈窗刪除。
  delFile(id) {
    uploadManager.setExternalState(id,this)
    if(this.fileSize &lt; 3000){
      return this.dom.parentNode.removeChild(this.dom)
    }
    if(window.confirm(`確定要刪除該檔案嗎?${this.fileName}`)){
      return this.dom.parentNode.removeChild(this.dom)
    }
  }
}
// 外部狀態
class uploadManager {
  static uploadDatabase = {}
  static add(id, uploadType, fileName, fileSize) {
    const filWeightObj = UploadFactory.create(uploadType)
    const dom = this.createDom(fileName, fileSize, () =&gt; {
      filWeightObj.delFile(id)
    })
    this.uploadDatabase[id] = {
      fileName,
      fileSize,
      dom
    }
  }
  // 建立DOM 並且為button繫結刪除事件。
  static createDom(fileName, fileSize, fn) {
    const dom = document.createElement('div')
    dom.innerHTML = `
      &lt;span&gt;檔名稱:${fileName},檔案大小:${fileSize}&lt;/span&gt;
      &lt;button class="delFile"&gt;刪除&lt;/button&gt;
    `
    dom.querySelector('.delFile').onclick = fn
    document.body.append(dom)
    return dom
  }
  static setExternalState(id, flyWeightObj) {
    const uploadData = this.uploadDatabase[id]
    for (const key in uploadData) {
      if (Object.hasOwnProperty.call(uploadData, key)) {
        flyWeightObj[key] = uploadData[key]
      }
    }
  }
}
// 定義一個工廠建立upload物件,如果其內部狀態範例物件存在直接返回,反之建立儲存並返回。
class UploadFactory {
  static createFlyWeightObjs = {}
  static create(uploadType) {
    if (this.createFlyWeightObjs[uploadType]) {
      return this.createFlyWeightObjs[uploadType]
    }
    return this.createFlyWeightObjs[uploadType] = new Upload(uploadType)
  }
}
// 開始載入
const startUpload = (uploadType, files)=&gt;{
    for (let i = 0, file; file = files[i++];) {
      uploadManager.add(++id, uploadType, file.fileName, file.fileSize)
    }
}
startUpload('plugin', [
  {fileName: '1.txt',fileSize: 1000},
  {fileName: '2.html',fileSize: 3000},
  {fileName: '3.txt',fileSize: 5000}
]);
startUpload('flash', [
  {fileName: '4.txt',fileSize: 1000},
  {fileName: '5.html',fileSize: 3000},
  {fileName: '6.txt',fileSize: 5000}
]);

17.職責鏈模式

  • 職責鏈模式的定義是:使用多個物件都有機會處理請求,從而避免請求的傳送者和接收者之間的耦合關係,將這些物件鏈成一條鏈,並沿著這條鏈傳遞該請求,知道有一個物件處理它為止。

優點

  • 降低耦合度,它將請求的傳送者和接收者解耦。
  • 簡化了物件,使得物件不需要知道鏈的結構。
  • 增強給物件指派職責的靈活性。通過改變鏈內的成員或者調動它們的次序,允許動態地新增或者刪除責任。

缺點

  • 不能保證每一條請求都一定被接收。
  • 系統效能將受到一定影響,而且在進行程式碼偵錯時不太方便,可能會造成迴圈呼叫。
  • 可能不容易觀察執行時的特徵,有礙於排除問題。

例子

  • 假設我們負責一個手機售賣的電商網站,分別繳納500元和200元定金的兩輪預訂後,會分別收到100元和50元的優惠券,而沒有支付定金的則視為普通購買,沒有優惠券,並且在庫存有限的情況下也無法保證能購買到。
class Order500 {
    constructor(){
        this.orderType = 1
    }
    handle(orderType, pay, stock){
        if(orderType === this.orderType&&pay){
            console.log('500元定金預約,得到100元優惠券')
        }else {
            return 'nextSuccessor'
        }
    }
}
class Order200 {
    constructor(){
        this.orderType = 2
    }
    handle(orderType, pay, stock){
        if(orderType === this.orderType&&pay){
            console.log('200元訂金預約,得到50元優惠卷')
        }else {
            return 'nextSuccessor'
        }
    }
}
class OrderNormal {
    constructor(){
        this.stock = 0
    }
    handle(orderType, pay, stock){
        if (stock > this.stock) {
            console.log('普通購買,無優惠卷')
          } else {
            console.log('手機庫存不足')
          }
    }
}
class Chain {
  constructor(order){
    this.order = order
    this.successor = null
  }
  setNextSuccessor(successor){
    return this.successor = successor
  }
  passRequest(...val){
    const ret = this.order.handle.apply(this.order,val)
    if(ret === 'nextSuccessor'){
      return this.successor&&this.successor.passRequest.apply(this.successor,val)
    }
    return ret
  }
}
console.log(new Order500())
var chainOrder500 = new Chain( new Order500() );
var chainOrder200 = new Chain( new Order200() );
var chainOrderNormal = new Chain( new OrderNormal() );
chainOrder500.setNextSuccessor( chainOrder200 );
chainOrder200.setNextSuccessor( chainOrderNormal );
chainOrder500.passRequest( 1, true, 500 ); // 輸出:500 元定金預購,得到 100 優惠券
chainOrder500.passRequest( 2, true, 500 ); // 輸出:200 元定金預購,得到 50 優惠券
chainOrder500.passRequest( 3, true, 500 ); // 輸出:普通購買,無優惠券
chainOrder500.passRequest( 1, false, 0 ); // 輸出:手機庫存不足

18.中介模式

  • 中介者模式的作用就是解除物件與物件之間的緊密耦合關係。增加一箇中介者物件之後,所有相關物件都通過中介者物件來通訊,而不是相互參照,所以當一個物件發生改變時,只需要通過中介者物件即可。中介者使各物件之間耦合鬆散,而且可以獨立改變他們之間的互動。中介者模式使網狀的多對多關係變成了相對簡單的一對多關係。

優點

  • 降低了類的複雜度,將一對多轉化成了一對一。各個類之間的解耦。

缺點

  • 當中介者變得龐大複雜,導致難以維護。

例子

// html-----------
選擇顏色:<select name="" id="colorSelect">
    <option value="">請選擇</option>
    <option value="red">紅色</option>
    <option value="blue">藍色</option>
  </select>
  <br />
  選擇記憶體:<select name="" id="memorySelect">
    <option value="">請選擇</option>
    <option value="32G">32G</option>
    <option value="63G">64G</option>
  </select>
  <br />
  輸入購買數量:<input type="text" id="numberInput" />
  <br />
  <div>你選擇了顏色:<span id="colorInfo"></span></div>
  <div>你選擇了記憶體:<span id="memoryInfo"></span></div>
  <div>你選擇了數量:<span id="numberInfo"></span></div>
  <button id="nextBtn" disabled="true">請選擇手機顏色和購買數量</button>
  // js -------------------
const goods = {
      "red|32G": 3,
      "red|16G": 0,
      "blue|32G": 1,
      "blue|16G": 6
    },
      colorSelect = document.getElementById('colorSelect'),
      memorySelect = document.getElementById('memorySelect'),
      numberInput = document.getElementById('numberInput'),
      colorInfo = document.getElementById('colorInfo'),
      memoryInfo = document.getElementById('memoryInfo'),
      numberInfo = document.getElementById('numberInfo'),
      nextBtn = document.getElementById('nextBtn'),
      mediator = (function () {
        return {
          changed(obj) {
            const color = colorSelect.value,
              memory = memorySelect.value,
              number = numberInput.value,
              stock = goods[`${color}|${memory}`]
            if (obj === colorSelect) {
              colorInfo.innerHTML = color
            } else if (obj === memorySelect) {
              memoryInfo.innerHTML = memory
            } else if (obj === numberInput) {
              numberInfo.innerHTML = number
            }
            if (!color) {
              nextBtn.disabled = true
              nextBtn.innerHTML = '請選擇手機顏色'
              return
            }
            if (!memory) {
              nextBtn.disabled = true
              nextBtn.innerHTML = '請選擇記憶體大小'
              return
            }
            if (Number.isInteger(number - 0) && number < 1) {
              nextBtn.disabled = true
              nextBtn.innerHTML = '請輸入正確的購買數量'
              return
            }
            nextBtn.disabled = false
            nextBtn.innerHTML = '放入購物車'
          }
        }
      })()
    colorSelect.onchange = function () {
      mediator.changed(this)
    }
    memorySelect.onchange = function () {
      mediator.changed(this)
    }
    numberInput.oninput = function () {
      mediator.changed(this)
    }

19.原型模式

  • 原型模式是指原型範例指向建立物件的種類,通過拷貝這些原型來建立新的物件,說白了就是克隆自己,生成一個新的物件。

優點

  • 不再依賴建構函式或者類建立物件,可以將這個物件作為一個模板生成更多的新物件。

缺點

  • 對於包含參照型別值的屬性來說,所有範例在預設的情況下都會取得相同的屬性值。

例子

const user = {
    name:'小明',
    age:'30',
    getInfo(){
        console.log(`姓名:${this.name},年齡:${this.age}`)
    }
}
const xiaozhang = Object.create(user)
xiaozhang.name = '小張'
xiaozhang.age = 18
xiaozhang.getInfo() // 姓名:小張,年齡:18
user.getInfo() // 姓名:小明,年齡:30

20.備忘錄模式

  • 備忘錄模式就是在不破壞封裝的前提下,捕獲一個物件內部狀態,並在該物件之外儲存這個狀態,以保證以後可以將物件恢復到原先的狀態。

優點

  • 給使用者提供了一種可以恢復狀態的機制,可以使使用者能夠比較方便地回到某個歷史的狀態。
  • 實現了資訊的封裝,使得使用者不需要關心狀態的儲存細節。

缺點

  • 如果類的成員變數過多,勢必會佔用比較大的資源,而且每一次儲存都會消耗一定的記憶體。

例子

// 棋子
class ChessPieces {
  constructor(){
    this.chess = {}
  }
  // 獲取棋子
  getChess(){
    return this.chess
  }
}
// 記錄棋路
class Record {
  constructor(){
    this.chessTallyBook = [] // 記錄棋路
  }
  recordTallyBook(chess){
    // console.log(this.chessTallyBook.includes(chess))
    const isLoadtion = this.chessTallyBook.some(
      item=>item.location === chess.location
    )
    if(isLoadtion){
      console.log(`${chess.type},${chess.location}已存在其他棋子`)
    }else {
      this.chessTallyBook.push(chess)
    }
    // this.chessTallyBook.some(item=>item.location === chess.location)
  }
  getTallyBook(){
    return this.chessTallyBook.pop()
  }
}
// 下棋規則
class ChessRule {
  constructor(){
    this.chessInfo = {}
  }
  playChess(chess){
    this.chessInfo = chess
  }
  getChess(){
    return this.chessInfo
  }
  // 記錄棋路
  recordTallyBook(){
    return new ChessPieces(this.chessInfo)
  }
  // 悔棋
  repentanceChess(chess){
    this.chessInfo = chess.getTallyBook()
  }
}
const chessRule = new ChessRule()
const record = new Record()
chessRule.playChess({
  type:'黑棋',
  location:'X10,Y10'
})
record.recordTallyBook(chessRule.getChess())//記錄棋路
chessRule.playChess({
  type:'白棋',
  location:'X11,Y10'
})
record.recordTallyBook(chessRule.getChess())//記錄棋路
chessRule.playChess({
  type:'黑棋',
  location:'X11,Y11'
})
record.recordTallyBook(chessRule.getChess())//記錄棋路
chessRule.playChess({
  type:'白棋',
  location:'X12,Y10'
})
console.log(chessRule.getChess())//{type:'白棋',location:'X12,Y10'}
chessRule.repentanceChess(record) // 悔棋
console.log(chessRule.getChess())//{type:'黑棋',location:'X11,Y11'}
chessRule.repentanceChess(record) // 悔棋
console.log(chessRule.getChess())//{type:'白棋',location:'X11,Y10'}

21.橋接模式

  • 橋接模式是指將抽象部分與它的實現部分分離,使它們各自獨立的變化,通過使用組合關係代替繼承關係,降低抽象和實現兩個可變維度的耦合度。

優點

  • 抽象和實現的分離。優秀的擴充套件能力。實現細節對客戶透明。

缺點

  • 橋接模式的引入會增加系統的理解與設計難度,由於聚合關聯關係建立在抽象層,要求開發者針對抽象進行設計與程式設計。

例子

  • 比如我們所用的手機,蘋果的iphoneX,和華為的mate40,品牌和型號就是它們共同的抽象部分,可以把他們單獨提取出來。
class Phone {
    constructor(brand,modle){
        this.brand = brand
        this.modle = modle
    }
    showPhone(){
        return `手機的品牌:${this.brand.getBrand()},型號${this.modle.getModle()}`
    }
}
class Brand {
    constructor(brandName){
        this.brandName = brandName
    }
    getBrand(){
        return this.brandName
    }
}
class Modle {
    constructor(modleName){
        this.modleName = modleName
    }
    getModle(){
        return this.modleName
    }
}
const phone = new Phone(new Brand('華為'),new Modle('mate 40'))
console.log(phone.showPhone())

22.存取者模式

  • 存取者模式是將資料的操作和資料的結構進行分離,對資料中各元素的操作封裝獨立的類,使其在不改變資料結構情況下擴充套件新的資料。

優點

  • 符合單一職責原則。具有優秀的擴充套件性和靈活性。

缺點

  • 違反了依賴倒置原則,依賴了具體類,沒有依賴抽象。

例子

    class Phone {
      accept() {
        throw new Error('子類的accept必須被重寫')
      }
    }
    class Mata40Pro extends Phone {
      accept() {
        const phoneVisitor = new PhoneVisitor()
        return phoneVisitor.visit(this)
      }
    }
    class IPhone13 extends Phone {
      accept() {
        const phoneVisitor = new PhoneVisitor()
        return phoneVisitor.visit(this)
      }
    }
    // 存取者類
    class PhoneVisitor {
      visit(phone) {
        if (phone.constructor === IPhone13) {
          return {
            os: 'ios',
            chip: 'A15仿生晶片',
            screen: '電容屏'
          }
        } else if (phone.constructor === Mata40Pro) {
          return {
            os: 'HarmonyOS',
            chip: 'Kirin 9000',
            GPUType: 'Mali-G78',
            port: 'type-c'
          }
        }
      }
    }
    const mata40Pro = new Mata40Pro()
    console.log(mata40Pro.accept())

23.直譯器模式

  • 直譯器模式提供了評估語言的語法或表示式的方式,它屬於行為型模式。這種模式實現了一個表示式介面該介面,該介面解釋一個特定的上下文。

優點

  • 可延伸性比較好,靈活。增加了新的解釋表示式的方式。

缺點

  • 可利用場景比較少,在web開發中幾乎不可見。對於複雜的環境比較難維護。
  • 直譯器模式會引起類膨脹。它還採用遞迴呼叫方法,沒控制好可能會導致崩潰。

例子

class TerminalExpression {
  constructor(data) {
    this.data = data
  }
  interpret(context) {
    if (context.indexOf(this.data) &gt; -1) {
      return true;
    }
    return false;
  }
}
class OrExpression {
  constructor(expr1, expr2) {
    this.expr1 = expr1;
    this.expr2 = expr2;
  }
  interpret(context) {
    return this.expr1.interpret(context) || this.expr2.interpret(context);
  }
}
class AndExpression {
  constructor(expr1, expr2) {
    this.expr1 = expr1;
    this.expr2 = expr2;
  }
  interpret(context) {
    return this.expr1.interpret(context) &amp;&amp; this.expr2.interpret(context);
  }
}
class InterpreterPatternDemo {
  static getMaleExpression() {
    const robert = new TerminalExpression("小明");
    const john = new TerminalExpression("小龍");
    return new OrExpression(robert, john);
  }
  static getMarriedWomanExpression() {
    const julie = new TerminalExpression("張三");
    const married = new TerminalExpression("小紅");
    return new AndExpression(julie, married);
  }
  static init(args) {
    const isMale = this.getMaleExpression();
    const isMarriedWoman = this.getMarriedWomanExpression();
    console.log(`小龍是男性?${isMale.interpret("小龍")}`)
    console.log(`小紅是一個已婚婦女?${isMarriedWoman.interpret("小紅 張三")}`)
  }
}
InterpreterPatternDemo.init()

總結

  • 以上是我歷時將近一個月的學習總結,然而一個月的時間是遠遠不夠的,在寫完這篇文章後,依舊對某些設計模式的應用場景缺乏瞭解。設計模式是要長時間深入研究的知識點,需要結合實際的場景去練習模仿,不斷的去思考。另外由於js的特性,很多設計模式在js中是殘缺的,非完全體的,強行用js去模仿傳統的設計模式顯的有點雞肋。但是隨著typescript的出現,設計模式在ts中可以無限接近傳統的設計模式,後續計劃寫一篇ts版本的設計模式部落格。
  • 本片文章學習來源:
    • 書籍《JavaScript設計模式與開發實踐》
    • 雙越老師的:《Javascript 設計模式系統講解與應用》
    • 以及百度搜尋借鑑的各種案例

以上就是javascript的23種設計模式總結大全的詳細內容,更多關於javascript設計模式的資料請關注it145.com其它相關文章!


IT145.com E-mail:sddin#qq.com