js 設計模式——狀態模式

来源:https://www.cnblogs.com/loveyt/archive/2019/08/24/11403784.html
-Advertisement-
Play Games

狀態模式 允許一個對象在其內部狀態改變時改變它的行為,對象看起來似乎修改了它的類。 簡單的解釋一下: 第一部分的意思是將狀態封裝成獨立的類,並將請求委托給當前的狀態對象,當對象的內部狀態改變時,會帶來不同的行為變化。 第二部分是從客戶的角度來看,我們使用的對象,在不同的狀態下具有截然不同的行為,這個 ...


狀態模式

允許一個對象在其內部狀態改變時改變它的行為,對象看起來似乎修改了它的類。

簡單的解釋一下:

  • 第一部分的意思是將狀態封裝成獨立的類,並將請求委托給當前的狀態對象,當對象的內部狀態改變時,會帶來不同的行為變化。
  • 第二部分是從客戶的角度來看,我們使用的對象,在不同的狀態下具有截然不同的行為,這個對象看起來是從不同的類中實例化而來的,實際上這是使用了委托的效果。

現在舉一個網上比較多的例子,沒錯就是電燈的例子(不要煩,請耐心往下看)

// 首先定義了一個Light類
class Light {
  // 定義一個狀態變數
  constructor(){
    this.state = 'off'
  }
  // 定義一個改變狀態的方法
  change(){
    if(this.state === 'off'){
      console.log('開燈')
      this.state = 'on'
    } else {
      console.log('關燈')
      this.state = 'off'
    }
  }
}
// 創建實例
let light = new Light()
// 調用方法
light.change()

噹噹噹噹,到此我們已經編寫了一個狀態機,邏輯簡單又縝密,看起來還有那麼點無懈可擊。BUT,你懂的事實並非如此,人生也沒那麼多的如意。隨著人類的進步,需求也不(de) 斷(cuo) 進(jin) 步(chi)(●'◡'●),於是新的電燈出現了,這電燈可厲害了,第一次點擊弱光,再次點擊強光,再點七彩光,再點emmm關了。

按我們上面的邏輯來寫,那可就刺激了:

  • 首先違反了開閉原則,每次改動都要更改change()方法,使得方法變得不穩定
  • 狀態切換的不明顯,無法一目瞭然的明白一共有多少種狀態
  • 狀態之間的切換關係,不過是往change()方法里添加if、else語句,是change()方法更加難閱讀和維護

那怎麼辦呢?有首歌怎麼唱來著“新的電燈已經出現,怎麼能夠停滯不前”,哈哈,所以狀態模式來了~~~

因為電燈的例子太無聊了,所以我換了一個例子但是呢意思是一樣滴:

  // 單曲迴圈類
  class SingleCycle{
    constructor(self){
      this._self = self
    }
    modeSwitch(){
      console.log('現在是單曲迴圈')
      this._self.setState( this._self.listCirculation )
    }
  }
  // 列表迴圈類
  class ListCirculation{
    constructor(self){
      this._self = self
    }
    modeSwitch(){
      console.log('現在是列表迴圈')
      this._self.setState( this._self.sequentialPlay )
    }
  }
  // 順序播放類
  class SequentialPlay{
    constructor(self){
      this._self = self
    }
    modeSwitch(){
      console.log('現在是順序播放')
      this._self.setState( this._self.shufflePlay )
    }
  }
  // 隨機播放類
  class ShufflePlay{
    constructor(self){
      this._self = self
    }
    modeSwitch(){
      console.log('現在是隨機播放')
      this._self.setState( this._self.singleCycle )
    }
  }
  // 音樂類
  class Music{
    constructor(){
      // 為每個狀態都創建一個狀態對象
      this.singleCycle = new SingleCycle(this)
      this.listCirculation = new ListCirculation(this)
      this.sequentialPlay = new SequentialPlay(this)
      this.shufflePlay = new ShufflePlay(this)
      // 定義初始狀態為順序播放
      this.currState = this.sequentialPlay
    }
    // 切換播放模式
    changeMode(){
      this.currState.modeSwitch()
    }
    // 下一次點擊時的播放狀態
    setState(newState){
      this.currState = newState;
    }
  }
  // 實例化音樂類
  let music = new Music()
  // 調用切換播放模式方法
  music.changeMode()

好了,到此我們改編完成,如果你沒有懵掉,good,如果你懵掉了,往下看:

  1. 首先我們定義了4個狀態類 SingleCycle(單曲迴圈)ListCirculation(列表迴圈)SequentialPlay(順序播放)ShufflePlay(隨機播放)
  2. 每個狀態類都定義了一個變數 _self 來接收 Music(音樂類) 傳過來的 this,還有一個方法 modeSwitch(狀態更改),用來改變下一次要播放的狀態
  3. 然後定義了一個 Music(音樂類) ,首先在裡面為每一個狀態都創建了一個狀態對象,還定義了一個變數 currState 來記錄下一次點擊時的狀態。
  4. 最後就是Music(音樂類)裡面定義的兩種方法 changeMode(切換播放模式)setState(下一次點擊時的播放狀態) 。當我們點擊切換模式的時候,在 changeMode(切換播放模式) 中去調用之前定義好的狀態類中的 modeSwitch(狀態更改) 方法,完成模式切換的同時調用Music(音樂類)中的 setState(下一次點擊時的播放狀態) 方法來對狀態進行改變,保證下一次點擊時切換不同的模式。

通過上面的方法可以看出:

  1. 我們可以在 Music(音樂類) 中清楚的知道一共有多少個狀態,同時 Music(音樂類) 中不再進行任何實質性的操作,而是通過 this.currState.modeSwitch() 交給了當前持有的狀態對象去執行
  2. 狀態的切換規律被事先在每一個狀態類中定義好了,在 Music(音樂類) 中沒有任何一個和狀態切換相關的條件分支語句

悄悄地說,上面的方法還闊以再完美一點呦


小小的拓展

通過上面的介紹我們瞭解到了每一個狀態類都有一個 modeSwitch() 方法,也就意味著我們每次添加狀態類都要寫一個方法,問題來了,人非聖賢,孰能無過?所以咧難免會丟掉的嘛!

然後做一些小小的優化:

// 定義一個State類
class State{
  constructor(self){
    this._self = self
  }
  modeSwitch(){
   throw new Error( '父類的 modeSwitch 方法必須被重寫' )
  }
}

// 狀態類(舉一個為例)

// 單曲迴圈類(繼承State類)
class SingleCycle extends State{
  modeSwitch(){
    console.log('現在是單曲迴圈')
    this._self.setState( this._self.listCirculation )
  }
}

好了完成,當某一天我們忘了寫方法,也能夠快速的定位到錯誤


目前對於狀態模式的理解就這麼多,以後有了新的理解會繼續更新的,溜了溜了(~ ̄▽ ̄)~


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原碼,項目中遇到的錯誤,解決方法,文章最後有鏈接可以獲取 項目簡介 功能描述 登陸,註冊,用戶一覽表,修改,刪除,添加,模糊查詢和精確查詢 採用的技術及環境 JSP:前端的信息展示 Servlet:業務邏輯功能實現,及調用資料庫的實現數據處理和傳輸 MySQL:用來實現數據存儲 利用Eclipse來 ...
  • 最近博客背景圖片的外鏈掛了,沒辦法,只好另找辦法。 在博客園後臺,有一個“文件”菜單,可以上傳自己的文件,我就打算把圖片傳到裡面。但卻發現了一個很反人性的設置:不允許上傳jpg,png文件,允許上傳的只有bmp,gif這樣的圖片文件。bmp文件太大,載入都要好幾秒,gif文件質量太差,只有256色, ...
  • 編程思想: 面向過程:凡事親力親為,所有事情的過程都要清楚,註重的是過程。 面向對象:提出需求,找到對象,對象解決這個問題,我們要結果,註重的是結果。 面向對象的特性:封裝,繼承,多態; JS: 是一門解釋性語言,是一門腳本語言,是一門弱類型語言,是一門基於對象的語言,是一門動態類型的語言。 對象: ...
  • 一、React的世界觀1、通過改變state來改變視圖視圖不用考慮如何改變自己,把state畫出來即可。2、變數不可變通過創建一個新的state來更改state,使得變更可追蹤,不容易因為其他部分修改state導致不可預測的錯誤3、結構與樣式分離參考了CSS的做法,RN的style機制使得代碼更清晰 ...
  • js中的數組和字元串有點類似,不是說本質上,而是在遍歷,獲取時的類似。從標識來說,可以一眼看出那個是數組,那個是字元串;但在使用遍歷時,會不經意的將兩者混淆,導致方法用錯。同時兩者的方法又有好幾個相同的,但需註意語義,以及有些方法是不會對原數組產生影響的。以下是我整理的一些關於數組和字元串的一些方法 ...
  • 介紹 在css2當中,存在標準模式下的盒子模型和IE下的怪異盒子模型。這兩種方案表示的是一種盒子模型的渲染模式。而在css3當中,新增加了彈性盒子模型,彈性盒子模型是一種新增加的強大的、靈活的佈局方案。彈性盒子模型是css3中新提出的一種佈局方案。是一種為了應對針對不同屏幕寬度不同設備的一整套新的布 ...
  • 一、什麼是 iframe iframe 用於在頁面內顯示頁面,使用 <iframe> 會創建包含另外一個文檔的內聯框架(即行內框架) 二、iframe 的常用屬性 1、width 定義 iframe 的寬度 2、height 定義 iframe 的高度 3、name 規定 iframe 的名稱 4、 ...
  • HTML5/CSS簡介 首先來說一說什麼是HTML5,HTML5可以認為是字面上的意義,也就是HTML的第五代產品,當然從另一個角度來說它是一種新的富客戶端解決方案。 HTML5 將成為 HTML、XHTML 以及 HTML DOM 的新標準。 HTML 的上一個版本誕生於 1999 年。自從那以後 ...
一周排行
    -Advertisement-
    Play Games
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...