前端常見的設計模式

来源:https://www.cnblogs.com/Yellow-ice/archive/2019/03/11/10513332.html
-Advertisement-
Play Games

今天主要介紹一下我們平常會經常用到的設計模式,設計模式總的來說有23種,而設計模式在前端中又該怎麼運用呢,接下來主要對比較前端中常見的設計模式做一個介紹 一、什麼是設計模式 一般來說,設計模式代表了最佳的實踐,通常被有經驗的面向對象的軟體開發人員所採用,在我們平時的軟體開發中,經常需要用到各種設計模 ...


  今天主要介紹一下我們平常會經常用到的設計模式,設計模式總的來說有23種,而設計模式在前端中又該怎麼運用呢,接下來主要對比較前端中常見的設計模式做一個介紹

 

一、什麼是設計模式

  一般來說,設計模式代表了最佳的實踐,通常被有經驗的面向對象的軟體開發人員所採用,在我們平時的軟體開發中,經常需要用到各種設計模式,設計模式是一套被反覆使用的、多數人知曉的、經過分類編目的、代碼設計經驗的總結,使用設計模式是為了重用代碼、讓代碼更容易被他人理解、保證代碼可靠性。

  設計模式可以說是軟體工程的基石,合理的使用設計模式,可以使我們的代碼真正的工程化,在項目中使用設計模式可以完美的解決很多問題,在設計模式中,大概來說總共有23種,而具體要用哪一種還需要根據情況而定,就像平時在前端開發中,我比較熟悉的就是工廠模式,原型模式和MVC這些模式啦,接下來主要對其中的一些設計模式進行一個比較詳細的介紹。

 

二、設計模式的分類

  首先,還是需要先說一下設計模式的分類,剛纔說到設計模式總的來說有23種,而這23種,又可以分為以下四大類

  1、創建型模式

創建型模式提供了一種在創建對象的同時隱藏創建邏輯的方式,而不是使用 new 運算符直接實例化對象,這使得程式在判斷針對某個給定實例需要創建哪些對象時更加靈活,主要包括以下幾種:

工廠模式、抽象工廠模式、單例模式、建造者模式、原型模式 

2、結構型模式

結構型模式關註類和對象的組合繼承的概念被用來組合介面和定義組合對象獲得新功能的方式,主要包括以下幾種:

適配器模式、橋接模式、過濾器模式、組合模式、裝飾器模式、外觀模式、享元模式、代理模式 

3、行為型模式

行為型模式關註對象之間的通信,主要包括以下幾種:

責任鏈模式、命令模式、解釋器模式、迭代器模式、中介者模式、備忘錄模式、觀察者模式、狀態模式、空對象模式、策略模式、模板模式、訪問者模式 

4、J2EE模式

J2EE模式關註表示層,這些模式是由 Sun Java Center 鑒定的,主要包括以下幾種:

MVC 模式、業務代表模式、組合實體模式、數據訪問對象模式、前端控制器模式、攔截過濾器模式、服務定位器模式、傳輸對象模式

 

三、設計模式六大原則

  上面介紹了幾種不同設計的模式,而所有的設計模式都需要遵循下麵的六大原則

1、開閉原則

開閉原則的意思是:對擴展開放,對修改關閉,在程式需要進行拓展的時候,不能去修改原有的代碼

2、里氏代換原則

里氏代換原則中說,任何基類可以出現的地方,子類一定可以出現,里氏代換原則是對開閉原則的補充,實現開閉原則的關鍵步驟就是抽象化,而基類與子類的繼承關係就是抽象化的具體實現,所以里氏代換原則是對實現抽象化的具體步驟的規範

3、依賴倒轉原則

這個原則是開閉原則的基礎,具體內容:針對介面編程,依賴於抽象而不依賴於具體 

4、介面隔離原則

這個原則的意思是:使用多個隔離的介面,比使用單個介面要好,它還有另外一個意思是:降低類之間的耦合度,此可見,其實設計模式就是從大型軟體架構出發、便於升級和維護的軟體設計思想,它強調降低依賴,降低耦合

5、最少知道原則

最少知道原則是指:一個實體應當儘量少地與其他實體之間發生相互作用,使得系統功能模塊相對獨立

6、合成復用原則

合成復用原則是指:儘量使用合成/聚合的方式,而不是使用繼承

 

四、常見的設計模式

  設計模式有很多種,接下來,我將介紹其中的幾種,並且介紹這些設計模式怎麼運用在前端中

  1、工廠模式

  工廠模式是用來創建對象的一種最常用的設計模式,我們不暴露創建對象的具體邏輯,而是將將邏輯封裝在一個函數中,那麼這個函數就可以被視為一個工廠,工廠模式根據抽象程度的不同可以分為:簡單工廠,工廠方法和抽象工廠,接下來,將對簡單工廠和工廠方法在JavaScript中的運用舉個簡單的例子

(1)簡單工廠

  簡單工廠模式又叫靜態工廠模式,由一個工廠對象決定創建某一種產品對象類的實例,主要用來創建同一類對象

  比如說,在實際的項目中,我們常常需要根據用戶的許可權來渲染不同的頁面,高級許可權的用戶所擁有的頁面有些是無法被低級許可權的用戶所查看,所以我們可以在不同許可權等級用戶的構造函數中,保存該用戶能夠看到的頁面。在根據許可權實例化用戶

let UserFactory = function (role) {
  function SuperAdmin() {
    this.name = "超級管理員",
    this.viewPage = ['首頁', '通訊錄', '發現頁', '應用數據', '許可權管理']
  }
  function Admin() {
    this.name = "管理員",
    this.viewPage = ['首頁', '通訊錄', '發現頁', '應用數據']
  }
  function NormalUser() {
    this.name = '普通用戶',
    this.viewPage = ['首頁', '通訊錄', '發現頁']
  }

  switch (role) {
    case 'superAdmin':
      return new SuperAdmin();
      break;
    case 'admin':
      return new Admin();
      break;
    case 'user':
      return new NormalUser();
      break;
    default:
      throw new Error('參數錯誤, 可選參數:superAdmin、admin、user');
  }
}

//調用
let superAdmin = UserFactory('superAdmin');
let admin = UserFactory('admin') 
let normalUser = UserFactory('user')

  在上面的例子中,UserFactory就是一個簡單工廠,在該函數中有3個構造函數分別對應不同的許可權的用戶,當我們調用工廠函數時,只需要傳遞superAdminadminuser這三個可選參數中的一個獲取對應的實例對象

  優點:簡單工廠的優點在於,你只需要一個正確的參數,就可以獲取到你所需要的對象,而無需知道其創建的具體細節

  缺點:在函數內包含了所有對象的創建邏輯(構造函數)和判斷邏輯的代碼,每增加新的構造函數還需要修改判斷邏輯代碼,我們的對象不是上面的3個而是30個或更多時,這個函數會成為一個龐大的超級函數,便得難以維護,簡單工廠只能作用於創建的對象數量較少,對象的創建邏輯不複雜時使用

 

(2)工廠方法

  工廠方法模式的本意是將實際創建對象的工作推遲到子類中,這樣核心類就變成了抽象類,但是在JavaScript中很難像傳統面向對象那樣去實現創建抽象類,所以在JavaScript中我們只需要參考它的核心思想即可,我們可以將工廠方法看作是一個實例化對象的工廠類

  比如說上面的例子,我們用工廠方法可以這樣寫,工廠方法我們只把它看作是一個實例化對象的工廠,它只做實例化對象這一件事情,我們採用安全模式創建對象

//安全模式創建的工廠方法函數
let UserFactory = function(role) {
  if(this instanceof UserFactory) {
    var s = new this[role]();
    return s;
  } else {
    return new UserFactory(role);
  }
}

//工廠方法函數的原型中設置所有對象的構造函數
UserFactory.prototype = {
  SuperAdmin: function() {
    this.name = "超級管理員",
    this.viewPage = ['首頁', '通訊錄', '發現頁', '應用數據', '許可權管理']
  },
  Admin: function() {
    this.name = "管理員",
    this.viewPage = ['首頁', '通訊錄', '發現頁', '應用數據']
  },
  NormalUser: function() {
    this.name = '普通用戶',
    this.viewPage = ['首頁', '通訊錄', '發現頁']
  }
}

//調用
let superAdmin = UserFactory('SuperAdmin');
let admin = UserFactory('Admin') 
let normalUser = UserFactory('NormalUser')

  在簡單工廠中,如果我們新增加一個用戶類型,需要修改兩個地方的代碼,一個是增加新的用戶構造函數,一個是在邏輯判斷中增加對新的用戶的判斷,而在抽象工廠方法中,我們只需要在UserFactory.prototype中添加就可以啦

 

2、代理模式

  代理模式主要是為其他對象提供一種代理以控制對這個對象的訪問,主要解決在直接訪問對象時帶來的問題,比如說:要訪問的對象在遠程的機器上,在面向對象系統中,有些對象由於某些原因(比如對象創建開銷很大,或者某些操作需要安全控制,或者需要進程外的訪問),直接訪問會給使用者或者系統結構帶來很多麻煩,我們可以在訪問此對象時加上一個對此對象的訪問層

  代理模式最基本的形式是對訪問進行控制,代理對象和另一個對象(本體)實現的是同樣的介面,實際上工作還是本體在做,它才是負責執行所分派的任務的那個對象或類,代理對象所做的不外乎節制對本體的訪問,代理對象並不會在另一對象的基礎上添加方法或修改其方法,也不會簡化那個對象的介面,它實現的介面與本體完全相同,所有對它進行的方法調用都會被傳遞給本體

(function(){
    // 示例代碼
     
    // 目標對象,是真正被代理的對象
    function Subject(){}
    Subject.prototype.request = function(){};
 
    /**
     * 代理對象
     * @param {Object} realSubject [持有被代理的具體的目標對象]
     */
    function Proxy(realSubject){
        this.realSubject = readSubject;
    }
    Proxy.prototype.request = function(){
        this.realSubject.request();
    };
}());

  在上面的代碼中,Proxy可以控制對真正被代理對象的一個訪問,在代理模式中,比較常見的就是虛擬代理,虛擬代理用於控制對那種創建開銷很大的本體的訪問,它會把本體的實例化推遲到有方法被調用的時候,比如說,現在我們假設PublicLibrary的實例化很慢,不能在網頁載入的時候立即完成,我們可以為其創建一個虛擬代理,讓它把PublicLibrary的實例化推遲到必要的時候,比如說我們在前端中經常用到的圖片懶載入,就可以用虛擬代理

 

3、觀察者模式

  如果大家學過一些像vue,react這些框架,相信大家對觀察者模式一定很熟悉,現在很多mvvm框架都用到了觀察者模式這個思想,觀察者模式又叫做發佈—訂閱模式,它定義對象間的一種一對多的依賴關係,當一個對象的狀態發生改變時,所有依賴於它的對象都將得到通知和更新,觀察者模式提供了一個訂閱模型,其中對象訂閱事件併在發生時得到通知,這種模式是事件驅動的編程基石,它有利益於良好的面向對象的設計

  下麵舉個例子,比如我們給頁面中的一個dom節點綁定一個事件,其實就可以看做是一種觀察者模式

document.body.addEventListener("click", function() {
    alert("Hello World")
},false )
document.body.click() //模擬用戶點擊

  在上面的例子中,需要監聽用戶點擊 document.body 的動作,但是我們是沒辦法預知用戶將在什麼時候點擊的,因此我們訂閱了 document.body 的 click 事件,當 body 節點被點擊時,body 節點便會向訂閱者發佈 "Hello World" 消息

 

4、單例模式

  單例模式保證一個類僅有一個實例,並提供一個訪問它的全局訪問點,保證一個類只有一個實例,實現的方法一般是先判斷實例存在與否,如果存在直接返回,如果不存在就創建了再返回,這就確保了一個類只有一個實例對象

  下麵舉個例子,在js中,我們可以使用閉包來創建實現這種模式

var single = (function(){
    var unique;

    function getInstance(){
    // 如果該實例存在,則直接返回,否則就對其實例化
        if( unique === undefined ){
            unique = new Construct();
        }
        return unique;
    }

    function Construct(){
        // ... 生成單例的構造函數的代碼
    }

    return {
        getInstance : getInstance
    }
})();

  在上面的代碼中,我們可以使用single.getInstance來獲取到單例,並且每次調用均獲取到同一個單例,在我們平時的開發中,我們也經常會用到這種模式,比如當我們單擊登錄按鈕的時候,頁面中會出現一個登錄框,而這個浮窗是唯一的,無論單擊多少次登錄按鈕,這個浮窗只會被創建一次,因此這個登錄浮窗就適合用單例模式

 

5、策略模式

  策略模式指的是定義一些列的演算法,把他們一個個封裝起來,目的就是將演算法的使用與演算法的實現分離開來,避免多重判斷條件,更具有擴展性

  下麵也是舉個例子,現在超市有活動,vip為5折,老客戶3折,普通顧客沒折,計算最後需要支付的金額,如果不使用策略模式,我們的代碼可能和下麵一樣

function Price(personType, price) {
    //vip 5 折
    if (personType == 'vip') {
        return price * 0.5;
    } 
    else if (personType == 'old'){ //老客戶 3 折
        return price * 0.3;
    } else {
        return price; //其他都全價
    }
}

  在上面的代碼中,我們需要很多個判斷,如果有很多優惠,我們又需要添加很多判斷,這裡已經違背了剛纔說的設計模式的六大原則中的開閉原則了,如果使用策略模式,我們的代碼可以這樣寫

// 對於vip客戶
function vipPrice() {
    this.discount = 0.5;
}
 
vipPrice.prototype.getPrice = function(price) {
  return price * this.discount;
}
// 對於老客戶
function oldPrice() {
    this.discount = 0.3;
}
 
oldPrice.prototype.getPrice = function(price) {
    return price * this.discount;
}
// 對於普通客戶
function Price() {
    this.discount = 1;
}
 
Price.prototype.getPrice = function(price) {
    return price ;
}

// 上下文,對於客戶端的使用
function Context() {
    this.name = '';
    this.strategy = null;
    this.price = 0;
}
 
Context.prototype.set = function(name, strategy, price) {
    this.name = name;
    this.strategy = strategy;
    this.price = price;
}
Context.prototype.getResult = function() {
    console.log(this.name + ' 的結賬價為: ' + this.strategy.getPrice(this.price));
}

var context = new Context();
var vip = new vipPrice();
context.set ('vip客戶', vip, 200);
context.getResult();   // vip客戶 的結賬價為: 100

var old = new oldPrice();
context.set ('老客戶', old, 200);
context.getResult();  // 老客戶 的結賬價為: 60

var Price = new Price();
context.set ('普通客戶', Price, 200);
context.getResult();  // 普通客戶 的結賬價為: 200

  在上面的代碼中,通過策略模式,使得客戶的折扣與演算法解藕,又使得修改跟擴展能獨立的進行,不影到客戶端或其他演算法的使用

  當我們的代碼中有很多個判斷分支,每一個條件分支都會引起該“類”的特定行為以不同的方式作出改變,這個時候就可以使用策略模式,可以改進我們代碼的質量,也更好的可以進行單元測試

 

  今天就寫到這裡了,其實還有很多設計模式,在這裡還沒有進行總結,大家有空的話也可以自己去瞭解

 

 

 

 


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

-Advertisement-
Play Games
更多相關文章
  • rxretrofitlibrary是一個已經寫好的網路框架庫,先以本地Module導入到自己的項目中。 1、它的初始化操作大多在自定義的application中完成,如: public class App extends Application { @Override public void onC ...
  • 【說明】 1、主界面上添加父容器:FragmentTabHost 2、顯示內容區域 3、導航區域 【註意】 1、指定id時為android:id/tabhost,綁定時使用android.R.id.tabhost. 2、每一個Tab對應的Fragment的會填充到tabcontext上 【效果】 【 ...
  • 今天我們來聊一聊有關AppCompat,作為Android Jetpack系列文章的開篇。說到Android Jetpack,我們先看一下這張圖: 從圖中我們可以看到,整個Android Jetpack分為了四大部分,而我們今天要講述的就是Foundation中的AppCompat小節,官方將該部分 ...
  • 實現波浪效果view,可以自定義view,也可以自定義drawable,我個人比較喜歡重寫drawable,因此這裡是自定義drawable實現效果,費話少說,先看效果。 這裡用了兩種方式實現波浪效果,一種是通過正弦函數去畫路徑,一種是通過三階貝塞爾曲線畫出類似正弦曲線的效果先看看實現波浪效果需要用 ...
  • 本人屬於自學上路,目前是在入門階段,所以有些內容實質性比較少,請各位大佬多多包涵。 視圖view的基本屬性: layout_margin:定義視圖與周圍視圖之間的空白距離 layout_padding:是指當前視圖與內部內容的距離 線性佈局LinearLayout layout_gravity:指定 ...
  • 主攻前文 "吳烜:JavaScript實現ZLOGO: 界面改進與速度可調" 的幾個性能問題 線上演示: "圈3" 源碼仍在: "program in chinese/quan3" 之前是在繪製過程中計算每幀需要繪製的線段, 在嘗試改進的過程中很快發現問題太多且不易測試. 接著在某早晨"醒悟"到應該 ...
  • 續前文 "JavaScript實現ZLOGO: 前進方向和速度" 線上演示地址: http://codeinchinese.com/%E5%9C%883/%E5%9C%883.html 源碼仍在: "program in chinese/quan3" 主要功能改進是在界面可以選擇速度. 其他界面佈局 ...
  • 組件 組件之間的數據是單向綁定的。 父組件向子組件通信 是通過子組件定義的props屬性來實現。通過props定義變數與變數類型和驗證方式。 props簡化定義 在簡化定義中,變數是以數組的方式定義。 props完整定義 完整定義是採用字面量形式,type 要定義的類型 通信方式 父組件向子組件定義 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...