iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 1
0
Modern Web

JS Design Pattern 系列 第 1

JS Design Pattern Day01-單例模式 Singleton

嗨大家好,這系列主題都是從一本叫做 "JavaScript設計模式與開發實踐" 這本書範例練習來的,裡面講了十幾種設計模式以及一些哩哩叩叩的東西,反正就盡量寫完30篇。

今天就開始寫第一種模式:單例模式Singleton。

首先,單例模式的定義:保證一個類別僅有一個實例,並提供一個存取它的全域存取點,就網頁實際應用來說最常見大概就是建立一個元素然後需要被重複共用,像是loading page、警告視窗或是單頁網頁中常用的背景,或是系統中唯一的物件像是thread pool、特定的queue之類的。
舉個例子,現在要做一個簡單的登入頁面,點擊‘登入’按鈕之後彈出一個登入視窗。那大概可以這樣做:

//產生一個登入按鈕
var $button = (function() {
	return $('<button>').text('登入').appendTo('body');
})();

//產生一個登入畫面
var $loginLayer = (function() {
	var $layer = $('<div>').text('我是登入畫面').appendTo('body');
	$layer.hide();
	return $layer;
})();

//綁定按鈕事件
$button.click(showLayer);

//顯示登入畫面 
function showLayer() {
	$loginLayer.show();
}

這樣寫廣義來說就有點單例概念了,登入頁面不會因為多次點擊登入按鈕而重複的被產生,但是這樣寫有個缺點:還沒點擊登入按鈕登入畫面就被建立好了。可能有些人只是想瀏覽沒有要登入,因此多浪費一些資源。
所以改寫一下:點擊按鈕才開始製作,並且利用全域變數來判斷:

var $button = (function() {
	return $('<button>').text('登入').appendTo('body');
})();

var $loginLayer;

function createLayer() {
	if ($loginLayer) {
		return $loginLayer;
	} else {
		var $layer = $('<div>').text('我是登入畫面').appendTo('body');
		$layer.hide();
		return $layer;
	}
}

function showLayer() {
	$loginLayer = createLayer();
	$loginLayer.show();
}
$button.click(showLayer);

這樣寫還是有些小缺點:
1.違反單一職責原則,建立和管理都在同一個function裡(createLayer)
2.如果要製造其他物件的獨體,除了製作div那段之外其他幾乎都要全部複製一次
所以最後再改寫一下,做一個通用的單例模式:
首先利用getSingle函式來做單例判斷的行為,getSingle內部可以先用比較簡單的寫法

function getSingle(fn) {
	var singleItem;
	return function() {
		return singleItem || (singleItem = fn.apply(this));
	}
}

將單例實體的判斷與製作都交給這個function,因此可以帶入任何製作的function,再來我們將製作登入介面function準備好,這個function只管製作不用管是否單例

function createLayer(text) {
	var $layer = $('<div>').text('我是登入畫面').appendTo('body');
	$layer.hide();
	return $layer;
}

最後就是將製作登入畫面的createLayer丟入getSingle得到一個單例物件,就可以來控制這個物件了

var createSingleLoginLayer = getSingle(createLayer);

function showLayer() {
	createSingleLoginLayer().show();
}

最後完整的code:

var $button = (function() {
	return $('<button>').text('登入').appendTo('body');
})();

function getSingle(fn) {
	var singleItem;
	var argTmp = [].slice.call(arguments);
	var newArg = (function() {
		argTmp.shift();
		return argTmp;
	})();
	return function() {
		return singleItem || (singleItem = fn.apply(this, newArg));
	}
}

function createLayer(text) {
	var $layer = $('<div>').text(text).appendTo('body');
	$layer.hide();
	return $layer;
}

var createSingleLoginLayer = getSingle(createLayer,'我是登入畫面');

function showLayer() {
	createSingleLoginLayer().show();
}
$button.click(showLayer);

好以上就是單例模式啦,其實getSingle這樣的寫法也有點代理模式的感覺,之後也會練習到(如果我有寫到那天的話)!


下一篇
JS Design Pattern Day02-策略模式 Strategy
系列文
JS Design Pattern 30

尚未有邦友留言

立即登入留言