輕鬆掌握:JavaScript代理模式、中介者模式

来源:http://www.cnblogs.com/susufufu/archive/2016/08/25/5808433.html
-Advertisement-
Play Games

代理模式、中介者模式 代理模式 在面向對象設計中,有一個單一職責原則,指就一個類(對象、函數)而言,應該僅有一個引起它變化的原因。如果一個對象承擔了過多的職責,就意味著它將變得巨大,引起它變化的原因就多,它把這些職責耦合到了一起,這種耦合會導致程式難於維護和重構。 這時候,我們可以把該對象(本體)的 ...


代理模式、中介者模式

代理模式

在面向對象設計中,有一個單一職責原則,指就一個類(對象、函數)而言,應該僅有一個引起它變化的原因。如果一個對象承擔了過多的職責,就意味著它將變得巨大,引起它變化的原因就多,它把這些職責耦合到了一起,這種耦合會導致程式難於維護和重構。

這時候,我們可以把該對象(本體)的其中一部分職責分離出來給一些第三方對象去做,本體只管自己的一些核心職責,這些第三方對象就稱作代理。代理對象可以作為對象(也叫“真正的主體”)的保護者,讓真正的主體對象做儘量少的工作。在代理設計模式中,一個對象充當了另一個對象的介面的角色。

通常代理和本體的介面應該保持一致性,這樣當不需要代理的時候,用戶可直接訪問本體。

當我們不方便直接訪問一個對象時,就可以考慮給該對象招一個代理。

代理可用於:圖片預載入、合併HTTP請求(代理收集一定時間內的所有HTTP請求,然後一次性發給伺服器)、惰性載入(通過代理處理和收集一些基本操作,然後僅在真正需要本體的時候才載入本體)、緩存代理(緩存請求結果、計算結果)等

例子1:圖片預載入

var myImage = (function(){
    var imgNode = document.createElement('img');
    document.body.appendChild(imgNode);
    return {
        setSrc:function(src){
            imgNode.src = src;
        }
    }
})();
//代理函數
var proxyImage = (function(){
    var img = new Image;
    img.onload = function(){
        myImage.setSrc(this.src);
    }
    return{
        setSrc:function(src){
            myImage.setSrc('loading.gif');
            img.src = src;
        }
    }
})();

proxyImage.setSrc('show.jpg');

中介者模式

中介者模式的作用就是解除對象與對象之間的緊耦合關係,它也稱‘調停者’。所有的對象都通過中介者對象來通信,而不是相互引用,所以當一個對象發生改變時,只需要通知中介者即可。

如:機場的指揮塔,每架飛機都只需要和指揮塔通信即可,指揮塔知道每架飛機的飛行狀況,可以安排所有起降時間,調整航線等

中介者模式符合迪米特法則,即最少知識原則,指一個對象應該儘可能少地瞭解另外的對象。如果對象之間的耦合性太高,則改變一個對象,會牽動很多對象,難於維護。當對象耦合很緊時,要修改一個對象而不影響其它的對象是很困難的。

如果對象之間的複雜耦合確實導致調用和維護出現了困難,而且這些耦合度隨項目的變化呈指數增長,那我們就可以考慮用中介者模式來重構代碼!中介者通過解耦來提升代碼的可維護性。

例子1:游戲

玩家對象是通過Player()構造函數來創建的,有自己的points和name屬性。原型上的play()方法負責給自己加一分然後通知中介者:

function Player(name) {
    this.points = 0;
    this.name = name;
}
Player.prototype.play = function () {
    this.points += 1;
    mediator.played();
};

scoreboard對象(計分板)有一個update()方法,它會在每次玩家玩完後被中介者調用。計分析根本不知道玩家的任何信息,也不保存分數,它只負責顯示中介者給過來的分數:

var scoreboard = {
    element: document.getElementById('results'),
    update: function (score) {
        var i, msg = '';
        for (i in score) {
            if (score.hasOwnProperty(i)) {
                msg += '<p><strong>' + i + '<\/strong>: ';
                msg += score[i];
                msg += '<\/p>';
            }
        }
        this.element.innerHTML = msg;
    }
};

現在我們來看一下mediator對象(中介者)。在游戲初始化的時候,在setup()方法中創建游戲者,然後放後players屬性以便後續使用。played()方法會被游戲者在每輪玩完後調用,它更新score哈希然表然後將它傳給scoreboard用於顯示。最後一個方法是keypress(),負責處理鍵盤事件,決定是哪位玩家玩的,並且通知它:

var mediator = {
    players: {},
    setup: function () {
        var players = this.players;
        players.home = new Player('Home');
        players.guest = new Player('Guest');
    },
    played: function () {
        var players = this.players,
        score = {
            Home: players.home.points,
            Guest: players.guest.points
        };
        scoreboard.update(score);
    },
    keypress: function (e) {
        e = e || window.event; // IE
        if (e.which === 49) { // key "1"
            mediator.players.home.play();
            return;
        }
        if (e.which === 48) { // key "0"
            mediator.players.guest.play();
            return;
        }
    }
};

最後一件事是初始化和結束游戲:

// go!
mediator.setup();
window.onkeypress = mediator.keypress;

// game over in 30 seconds
setTimeout(function () {
    window.onkeypress = null;
    alert('Game over!');
}, 30000);

例子2:賣手機

var goods = {   //庫存
    'red|32G':3,
    'red|16G':5,
    'blue|32G':3,
    'blue|16G':6
}
//中介者
var mediator = (function(){
    function id(id){
        return document.getElementById(id);
    }
    var colorSelect = id('colorSelect'),
        memorySelect = id('memorySelect'),
        numberInput = id('numberInput'),
        colorInfo = id('colorInfo'),
        memoryInfo = id('memoryInfo'),
        numberInfo = id('numberInfo'),
        nextBtn = id('nextBtn');
    return{
        changed:function(obj){
            var 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 > 0){
                nextBtn.disabled = true;
                nextBtn.innerHTML = '請輸入正確的購買數量';
                return;
            }
            nextBtn.disabled = false;
            nextBtn.innerHTML = '放入購物車';
        }
    }
})();
//添加事件
colorSelect.onchange = function(){
    mediator.changed(this);
}
memorySelect.onchange = function(){
    mediator.changed(this);
}
numberInput.onchange = function(){
    mediator.changed(this);
}

參考文獻: 《JavaScript模式》 《JavaScript設計模式與開發實踐》


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

-Advertisement-
Play Games
更多相關文章
  • 一、封裝 目的:保護類,讓類更加安全。 做法:讓類裡面的成員變數變為私有(即訪問修飾符)的,做相應的方法或者屬性去間接的操作成員變數 ※訪問修飾符 private 私有的 只能在該類中訪問 protected 受保護的 只能在該類和它的子類中訪問 public 公有的 在任何地方都可以訪問 △封裝成 ...
  • 本文提供一個簡單的方法實現一個流程的進度條載入效果,以便在頁面中可以通過它來更好地反饋耗時任務的完成進度。要實現這個功能,首先要考慮怎樣實現一個靜態的進度條效果,類似下麵這樣的: 這個倒是比較簡單,兩個div即可,bootstrap官方就提供有多種主題的進度條組件。如果自己要用,參照下別人的代碼,寫... ...
  • http://images2015.cnblogs.com/blog/877509/201608/877509-20160826005432819-958173773.png ...
  • 觀察者模式 觀察者模式也叫“訂閱者/發佈者”模式,定義對象間的一種一對多的依賴關係,發佈者可以向所有訂閱者發佈消息。 觀察者模式被廣泛地應用於JavaScript客戶端編程中。所有的瀏覽器事件(mouseover,keypress等)都是使用觀察者模式的例子。 使用這個模式的最主要目的就是促進對象之 ...
  • 問題描述 最近在做移動項目時遇到一個頁面滾動穿透問題,具體場景是這樣的,在一個可滾動的列表頁中彈出一個蒙層,蒙層中的內容是可滾動的,底部的父頁面理論上是不可滾動的,但是當滑動蒙層內容時,底部父頁面會跟隨滾動,這就是頁面滾動穿透的問題。這個是比較常見的問題,基本都會遇到,Google一下出解決方案也是 ...
  • 1、安裝node-v6.3.0-x64,安裝成功後再點擊node-v6.3.0-x64卸載(點擊remove)。 2、安裝node-v4.4.7-x64。 3、打開cmd命令行,輸入node -v,查看下版本,如果有顯示版本,說明已經安裝成功。 4、輸入npm -v,查看下npm的版本,如果有顯示版 ...
  • 其實今天的分享很簡單,只要你簡單瞭解Jq拓展方法,只要你會遍歷元素,那就能自己封裝出來。在工作中正是因為有了一個個這樣的方法,大大提升了我們的工作效率,減小了失誤次數。但是我們往往又經常使用別人封裝好的方法,這就很不爽了。 希望熱愛技術的園友們今後都能做一個,自己封裝方法,給整個公司用的人。 再也不 ...
  • indexOf去重 Array.prototype.unique1 = function() { var arr = []; for (var i = 0; i < this.length; i++) { var item = this[i]; if (arr.indexOf(item) -1) { ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...