中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

JavaScript設計模式之中介者模式實例分析

發布時間:2022-08-10 09:41:34 來源:億速云 閱讀:110 作者:iii 欄目:開發技術

今天小編給大家分享一下JavaScript設計模式之中介者模式實例分析的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

    中介者模式

    在我們生活的世界中,每個人每個物體之間都會產生一些錯綜復雜的聯系。在應用程序里也是一樣,程序由大大小小的單一對象組成,所有這些對象都按照某種關系和規則來通信。

    平時我們大概能記住 10 個朋友的電話、30 家餐館的位置。在程序里,也許一個對象會和其他 10 個對象打交道,所以它會保持 10 個對象的引用。當程序的規模增大,對象會越來越多,它們之間的關系也越來越復雜,難免會形成網狀的交叉引用。當我們改變或刪除其中一個對象的時候,很可能需要通知所有引用到它的對象。這樣一來,就像在心臟旁邊拆掉一根毛細血管一般, 即使一點很小的修改也必須小心翼翼,如下圖所示。

    JavaScript設計模式之中介者模式實例分析

    面向對象設計鼓勵將行為分布到各個對象中,把對象劃分成更小的粒度,有助于增強對象的可復用性,但由于這些細粒度對象之間的聯系激增,又有可能會反過來降低它們的可復用性。

    中介者模式的作用就是解除對象與對象之間的緊耦合關系。增加一個中介者對象后,所有的相關對象都通過中介者對象來通信,而不是互相引用,所以當一個對象發生改變時,只需要通知中介者對象即可。中介者使各對象之間耦合松散,而且可以獨立地改變它們之間的交互。中介者模式使網狀的多對多關系變成了相對簡單的一對多關系,如下圖所示。

    JavaScript設計模式之中介者模式實例分析

    在前面的圖中,如果對象 A 發生了改變,則需要同時通知跟 A 發生引用關系的 B、D、E、F 這 4 個對象;而在上圖中,使用中介者模式改進之后,A 發生改變時則只需要通知這個中介者對象即可。

    現實中的中介者

    在現實生活中也有很多中介者的例子,例如機場指揮塔。

    中介者也被稱為調停者,我們想象一下機場的指揮塔,如果沒有指揮塔的存在,每一架飛機要和方圓 100 公里內的所有飛機通信,才能確定航線以及飛行狀況,后果是不可想象的。現實中的情況是,每架飛機都只需要和指揮塔通信。指揮塔作為調停者,知道每一架飛機的飛行狀況,所以它可以安排所有飛機的起降時間,及時做出航線調整。

    下面我們來看中介者模式在下面這個案例中的應用。

    中介者模式的例子

    泡泡堂游戲

    大家可能都還記得泡泡堂游戲,現在我們來一起回顧這個游戲,假設在游戲之初只支持兩個玩家同時進行對戰。

    先定義一個玩家構造函數,它有 3 個簡單的原型方法:Play.prototype.winPlay.prototype.lose 以及表示玩家死亡的 Play.prototype.die

    因為玩家的數目是 2,所以當其中一個玩家死亡的時候游戲便結束, 同時通知它的對手勝利。 這段代碼看起來很簡單:

    function Player(name) {
    	this.name = name
    	this.enemy = null; // 敵人
    };
    Player.prototype.win = function () {
    	console.log(this.name + ' won ');
    };
    Player.prototype.lose = function () {
    	console.log(this.name + ' lost');
    };
    Player.prototype.die = function () {
    	this.lose();
    	this.enemy.win();
    };

    接下來創建 2 個玩家對象:

    const player1 = new Player('玩家一');
    const player2 = new Player('玩家二');

    給玩家相互設置敵人:

    player1.enemy = player2; 
    player2.enemy = player1;

    當玩家 player1 被泡泡炸死的時候,只需要調用這一句代碼便完成了一局游戲:

    player1.die();// 輸出:玩家一 lost、玩家二 won

    然而真正的泡泡堂游戲至多可以有 8 個玩家,并分成紅藍兩隊進行游戲。

    為游戲增加隊伍

    現在我們改進一下游戲。因為玩家數量變多,用下面的方式來設置隊友和敵人無疑很低效:

    player1.partners = [player1, player2, player3, player4];
    player1.enemies = [player5, player6, player7, player8];
    Player5.partners = [player5, player6, player7, player8];
    Player5.enemies = [player1, player2, player3, player4];

    所以我們定義一個數組 players 來保存所有的玩家,在創建玩家之后,循環 players 來給每個玩家設置隊友和敵人:

    const players = [];

    再改寫構造函數 Player,使每個玩家對象都增加一些屬性,分別是隊友列表、敵人列表 、 玩家當前狀態、角色名字以及玩家所在的隊伍顏色:

    function Player(name, teamColor) {
    	this.partners = []; // 隊友列表
    	this.enemies = []; // 敵人列表
    	this.state = 'live'; // 玩家狀態
    	this.name = name; // 角色名字
    	this.teamColor = teamColor; // 隊伍顏色
    };

    玩家勝利和失敗之后的展現依然很簡單,只是在每個玩家的屏幕上簡單地彈出提示:

    Player.prototype.win = function () { // 玩家團隊勝利
    	console.log('winner: ' + this.name);
    };
    Player.prototype.lose = function () { // 玩家團隊失敗
    	console.log('loser: ' + this.name);
    };

    玩家死亡的方法要變得稍微復雜一點,我們需要在每個玩家死亡的時候,都遍歷其他隊友的生存狀況,如果隊友全部死亡,則這局游戲失敗,同時敵人隊伍的所有玩家都取得勝利,代碼如下:

    Player.prototype.die = function () { // 玩家死亡
    	let all_dead = true;
    	this.state = 'dead'; // 設置玩家狀態為死亡
    	for (let i = 0; i < this.partners.length; i++) { // 遍歷隊友列表
    		if (this.partners[i].state !== 'dead') { // 如果還有一個隊友沒有死亡,則游戲還未失敗
    			all_dead = false;
    			break;
    		}
    	}
    	if (all_dead === true) { // 如果隊友全部死亡
    		this.lose(); // 通知自己游戲失敗
    		for (let i = 0; i < this.partners.length; i++) { // 通知所有隊友玩家游戲失敗
    			this.partners[i].lose();
    		}
    		for (let i = 0; i < this.enemies.length; i++) { // 通知所有敵人游戲勝利
    			this.enemies[i].win();
    		}
    	}
    };

    最后定義一個工廠來創建玩家:

    const playerFactory = function (name, teamColor) {
    	const newPlayer = new Player(name, teamColor); // 創建新玩家
    	for (let i = 0; i < players.length; i++) { // 通知所有的玩家,有新角色加入
    		if (players[i].teamColor === newPlayer.teamColor) { // 如果是同一隊的玩家
    			players[i].partners.push(newPlayer); // 相互添加到隊友列表
    			newPlayer.partners.push(players[i]);
    		} else {
    			players[i].enemies.push(newPlayer); // 相互添加到敵人列表
    			newPlayer.enemies.push(players[i]);
    		}
    	}
    	players.push(newPlayer);
    	return newPlayer;
    };

    現在來感受一下, 用這段代碼創建 8 個玩家:

    //紅隊:
    var player1 = playerFactory('皮蛋', 'red'),
    	player2 = playerFactory('小乖', 'red'),
    	player3 = playerFactory('寶寶', 'red'),
    	player4 = playerFactory('小強', 'red');
    //藍隊:
    var player5 = playerFactory('黑妞', 'blue'),
    	player6 = playerFactory('蔥頭', 'blue'),
    	player7 = playerFactory('胖墩', 'blue'),
    	player8 = playerFactory('海盜', 'blue');

    讓紅隊玩家全部死亡:

    player1.die();
    player2.die();
    player4.die();
    player3.die();

    結果如下:

    loser: 寶寶
    loser: 皮蛋
    loser: 小乖
    loser: 小強
    winner: 黑妞
    winner: 蔥頭
    winner: 胖墩
    winner: 海盜

    玩家增多帶來的困擾

    現在我們已經可以隨意地為游戲增加玩家或者隊伍,但問題是,每個玩家和其他玩家都是緊緊耦合在一起的。在此段代碼中,每個玩家對象都有兩個屬性,this.partnersthis.enemies,用來保存其他玩家對象的引用。當每個對象的狀態發生改變,比如角色移動、吃到道具或者死亡時,都必須要顯式地遍歷通知其他對象。

    在這個例子中只創建了 8 個玩家,或許還沒有對你產生足夠多的困擾,而如果在一個大型網絡游戲中,畫面里有成百上千個玩家,幾十支隊伍在互相廝殺。如果有一個玩家掉線,必須從所有其他玩家的隊友列表和敵人列表中都移除這個玩家。游戲也許還有解除隊伍和添加到別的隊伍的功能,紅色玩家可以突然變成藍色玩家,這就不再僅僅是循環能夠解決的問題了。面對這樣的需求,我們上面的代碼可以迅速進入投降模式。

    用中介者模式改造泡泡堂游戲

    現在我們開始用中介者模式來改造上面的泡泡堂游戲, 改造后的玩家對象和中介者的關系如下圖所示。

    JavaScript設計模式之中介者模式實例分析

    首先仍然是定義 Player 構造函數和 player 對象的原型方法,在 player 對象的這些原型方法 中,不再負責具體的執行邏輯,而是把操作轉交給中介者對象,我們把中介者對象命名為 playerDirector

    function Player(name, teamColor) {
    	this.name = name; // 角色名字
    	this.teamColor = teamColor; // 隊伍顏色 
    	this.state = 'alive'; // 玩家生存狀態
    };
    Player.prototype.win = function () {
    	console.log(this.name + ' won ');
    };
    Player.prototype.lose = function () {
    	console.log(this.name + ' lost');
    };
    /*******************玩家死亡*****************/
    Player.prototype.die = function () {
    	this.state = 'dead';
    	playerDirector.reciveMessage('playerDead', this); // 給中介者發送消息,玩家死亡
    };
    /*******************移除玩家*****************/
    Player.prototype.remove = function () {
    	playerDirector.reciveMessage('removePlayer', this); // 給中介者發送消息,移除一個玩家
    };
    /*******************玩家換隊*****************/
    Player.prototype.changeTeam = function (color) {
    	playerDirector.reciveMessage('changeTeam', this, color); // 給中介者發送消息,玩家換隊
    };

    再繼續改寫之前創建玩家對象的工廠函數,可以看到,因為工廠函數里不再需要給創建的玩家對象設置隊友和敵人,這個工廠函數幾乎失去了工廠的意義:

    const playerFactory = function (name, teamColor) {
    	const newPlayer = new Player(name, teamColor); // 創造一個新的玩家對象
    	playerDirector.reciveMessage('addPlayer', newPlayer); // 給中介者發送消息,新增玩家
    	return newPlayer;
    };

    最后,我們需要實現這個中介者 playerDirector 對象,一般有以下兩種方式。

    • 利用發布&mdash;訂閱模式。將 playerDirector 實現為訂閱者,各 player 作為發布者,一旦 player 的狀態發生改變,便推送消息給 playerDirectorplayerDirector 處理消息后將反饋發送 給其他 player

    • playerDirector 中開放一些接收消息的接口,各 player 可以直接調用該接口來給 playerDirector 發送消息,player 只需傳遞一個參數給 playerDirector,這個參數的目的是使 playerDirector 可以識別發送者。同樣,playerDirector 接收到消息之后會將處理結果反饋給其他 player

    這兩種方式的實現沒什么本質上的區別。在這里我們使用第二種方式,playerDirector 開放一個對外暴露的接口 reciveMessage,負責接收 player 對象發送的消息,而 player 對象發送消息的時候,總是把自身 this 作為參數發送給 playerDirector,以便 playerDirector 識別消息來自于哪個玩家對象,代碼如下:

    const playerDirector = (function () {
    	const players = {}, // 保存所有玩家
    		operations = {}; // 中介者可以執行的操作
    	/**
    	 * 新增一個玩家
    	 * @param {Player} player 玩家
    	 */
    	operations.addPlayer = function (player) {
    		const teamColor = player.teamColor; // 玩家的隊伍顏色
    		// 如果該顏色的玩家還沒有成立隊伍,則新成立一個隊伍
    		players[teamColor] = players[teamColor] || [];
    		players[teamColor].push(player); // 添加玩家進隊伍
    	};
    	/**
    	 * 移除一個玩家
    	 * @param {Player} player 玩家
    	 */
    	operations.removePlayer = function (player) {
    		const teamColor = player.teamColor, // 玩家的隊伍顏色
    			teamPlayers = players[teamColor] || []; // 該隊伍所有成員
    		for (let i = teamPlayers.length - 1; i >= 0; i--) { // 遍歷刪除
    			if (teamPlayers[i] === player) {
    				teamPlayers.splice(i, 1);
    			}
    		}
    	};
    	/**
    	 * 玩家換隊
    	 * @param {Player} player 玩家
    	 * @param {string} newTeamColor 隊伍顏色
    	 */
    	operations.changeTeam = function (player, newTeamColor) { // 玩家換隊
    		operations.removePlayer(player); // 從原隊伍中刪除
    		player.teamColor = newTeamColor; // 改變隊伍顏色
    		operations.addPlayer(player); // 增加到新隊伍中
    	};
    	/**
    	 * 玩家死亡
    	 * @param {Player} player 玩家
    	 */
    	operations.playerDead = function (player) {
    		const teamColor = player.teamColor,
    			teamPlayers = players[teamColor]; // 玩家所在隊伍
    		let all_dead = true;
    		for (let i = 0; i < teamPlayers.length; i++) {
    			if (teamPlayers[i].state !== 'dead') {
    				all_dead = false;
    				break;
    			}
    		}
    		if (all_dead) { // 全部死亡
    			for (let i = 0; i < teamPlayers.length; i++) {
    				teamPlayers[i].lose(); // 本隊所有玩家 lose 
    			}
    			for (const color in players) {
    				if (color !== teamColor) {
    					const teamPlayers = players[color]; // 其他隊伍的玩家
    					for (let i = 0; i < teamPlayers.length; i++) {
    						teamPlayers[i].win(); // 其他隊伍所有玩家 win 
    					}
    				}
    			}
    		}
    	};
    	const reciveMessage = function () {
    		// arguments 的第一個參數為消息名稱
    		const message = Array.prototype.shift.call(arguments); 
    		operations[message].apply(this, arguments);
    	};
    	return {
    		reciveMessage
    	}
    })();

    可以看到,除了中介者本身,沒有一個玩家知道其他任何玩家的存在,玩家與玩家之間的耦合關系已經完全解除,某個玩家的任何操作都不需要通知其他玩家,而只需要給中介者發送一個消息,中介者處理完消息之后會把處理結果反饋給其他的玩家對象。我們還可以繼續給中介者擴展更多功能,以適應游戲需求的不斷變化。

    我們來看下測試結果:

    // 紅隊:
    var player1 = playerFactory('皮蛋', 'red'),
    	player2 = playerFactory('小乖', 'red'),
    	player3 = playerFactory('寶寶', 'red'),
    	player4 = playerFactory('小強', 'red');
    // 藍隊:
    var player5 = playerFactory('黑妞', 'blue'),
    	player6 = playerFactory('蔥頭', 'blue'),
    	player7 = playerFactory('胖墩', 'blue'),
    	player8 = playerFactory('海盜', 'blue');
    player1.die();
    player2.die();
    player3.die();
    player4.die();

    運行結果如下。

    皮蛋 lost
    小乖 lost
    寶寶 lost
    小強 lost
    黑妞 won
    蔥頭 won
    胖墩 won
    海盜 won

    假設皮蛋和小乖掉線

    player1.remove(); 
    player2.remove(); 
    player3.die(); 
    player4.die();

    則結果如下。

    寶寶 lost
    小強 lost
    黑妞 won
    蔥頭 won
    胖墩 won
    海盜 won

    假設皮蛋從紅隊叛變到藍隊

    player1.changeTeam( 'blue' ); 
    player2.die(); 
    player3.die(); 
    player4.die();

    則結果如下。

    小乖 lost
    寶寶 lost
    小強 lost
    黑妞 won
    蔥頭 won
    胖墩 won
    海盜 won
    皮蛋 won

    以上就是“JavaScript設計模式之中介者模式實例分析”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    定安县| 察隅县| 盐池县| 保山市| 农安县| 谢通门县| 郑州市| 天水市| 松江区| 紫阳县| 云南省| 嘉黎县| 凤凰县| 安西县| 青川县| 泽州县| 年辖:市辖区| 古交市| 定安县| 通化县| 保定市| 建瓯市| 德兴市| 武功县| 白朗县| 长顺县| 津市市| 成安县| 湟源县| 斗六市| 山丹县| 泰和县| 张家口市| 台东县| 武定县| 长岛县| 景洪市| 淅川县| 丰原市| 清涧县| 西青区|