iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 18
0
Modern Web

JS Design Pattern 系列 第 18

JS Design Pattern Day18-狀態模式 State(上)

第18天了,今天是禮拜五,我的心不是在下班就是在下班的路上。

狀態模式 我們走

狀態模式,直接舉例解釋,我們來實做一個電燈按鈕開關,按一下電燈是開,按一下是關

var Light = function() {
	this.state = 'off';
	this.button = null;
};

Light.prototype.init = function() {
	var self = this;
	$('<button>').text('開關').appendTo('body').click(function() {
		self.buttonWasPressed();
	});
};

Light.prototype.buttonWasPressed = function() {
	if (this.state === 'off') {
		console.log('開');
		this.state = 'on';
	} else {
		console.log('關');
		this.state = 'off';
	}
};

var light = new Light();
light.init();

看起來好像沒有什麼問題,那如果電燈功能是不只一種狀態呢?以目前的寫法假設電燈狀態有'弱光'、'強光'與'關登'三種,可能會這麼寫:

Light.prototype.buttonWasPressed = function() {
	if (this.state === 'off') {
		console.log('弱光');
		this.state = 'weakLight';
	} else if (this.state === 'weakLight') {
		console.log('強光');
		this.state = 'strongLight';
	} else {
		console.log('關燈');
		this.state = 'off';
	}
};

現在我們來分析一下缺點,首先是當有狀態增加時,都必須要改動buttonWasPressed內部的程式碼,這樣是違反開放-封閉原則。再來是這樣新增狀態下去buttonWasPressed會持續膨脹且if else分支會相當多。

現在我們用狀態模式來修改,一般來說通常的方法都會是封裝'方法',而狀態模式要的是封裝'狀態'
首先先是作關燈的狀態

var OffLightState = function(light) {
	this.light = light;
};
//按鈕被按之後要做的事
OffLightState.prototype.buttonWasPressed = function() {
	console.log('弱光');
	this.light.setState(this.light.weakLightState); //切換到弱光的狀態
};

再來是作弱光的狀態

var WeakLightState = function(light) {
	this.light = light;
};
WeakLightState.prototype.buttonWasPressed = function() {
	console.log('強光');
	this.light.setState(this.light.strongLightState); //切換到強光的狀態
};

最後是關燈的狀態

var StrongLightState = function(light) {
	this.light = light;
};
StrongLightState.prototype.buttonWasPressed = function() {
	console.log('關燈');
	this.light.setState(this.light.offLightState); //切換到關燈的狀態
};

現在我們來修改Light模組,我們不再用字處當作狀態,而是使用我們上面作的這些物件當作狀態

var Light = function() {
	this.offLightState = new OffLightState(this);
	this.weakLightState = new WeakLightState(this);
	this.strongLightState = new StrongLightState(this);
	this.button = null;
};
Light.prototype.init = function() {
	var self = this;
	this.current = this.offLightState; //設置初始狀態
	$('<button>').text('開關').appendTo('body').click(function() {
		self.current.buttonWasPressed();
	});
};

再來做設定狀態setState的功能,其實就是利用init裡面做的current狀態,把新的狀態指定到這個屬性上

Light.prototype.setState = function(newState) {
	this.current = newState;
};

實際用一下

var light = new Light();
light.init();

理論上結果跟原本的寫法是一樣的,這種寫法當有新的狀態要加入時,只要增加一個狀態類別,並且改一下前後關聯的狀態就好。

這時候我們再來看狀態模式的定義:允許一個物件在其內部狀態改變時改變它的行為,物件看起來似乎修改了它的類別。在這裡我們將Light物件的內部狀態(弱光、強光、關燈)封裝成獨立的類別,所以當物件內部狀態改變時,會有不同的效果。而對於這個物件的使用者而言,看起來是用了不同的類別來實作的。


上一篇
JS Design Pattern Day17-裝飾者模式 Decorator(下)
下一篇
JS Design Pattern Day19-狀態模式 State(下)
系列文
JS Design Pattern 30

尚未有邦友留言

立即登入留言