引用:(23條消息) 《Javascript設計模式與開發實踐》關于設計模式典型代碼的整理(下):模闆方法模式、享元模式、職責鍊模式、中介者模式、裝飾模式、狀态模式、擴充卡模式_
8、模闆方法模式:一種基于繼承的設計模式。
// 模闆方法模式
var Beverage = function (param) {
var boilWater = function () {
console.log('把水煮沸');
};
var brew = param.brew || function () {
throw new Error('必須傳遞brew方法');
};
var pourInCup = param.pourInCup || function () {
throw new Error( '必須傳遞 pourInCup 方法' );
};
var addCondiments = param.addCondiments || function () {
throw new Error( '必須傳遞 addCondiments 方法' );
};
var F = function(){};
F.prototype.init = function(){ boilWater();
brew();
pourInCup();
addCondiments();
};
return F;
};
var Coffee = Beverage({ brew: function(){
console.log( '用沸水沖泡咖啡' ); },
pourInCup: function(){
console.log( '把咖啡倒進杯子' );
},
addCondiments: function(){
console.log( '加糖和牛奶' ); }
});
var Tea = Beverage({
brew: function(){
console.log( '用沸水浸泡茶葉' ); },
pourInCup: function(){
console.log( '把茶倒進杯子' );
},
addCondiments: function(){
console.log( '加檸檬' );
}
});
// 測試
var coffee = new Coffee();
coffee.init();
var tea = new Tea();
tea.init();
9、享元模式:運用共享技術來有效支援大量細粒度的對象。
内部狀态與外部狀态:
内部狀态存儲于對象内部。
内部狀态可以被一些對象共享。
内部狀态獨立于具體的場景,通常不會改變。
外部狀态取決于具體的場景,并根據場景而變化,外部狀态不能被共享。
// 享元模式
var Model = function (sex) {
this.sex = sex; // 性别為内部狀态
};
Model.prototype.takePhoto = function () {
console.log('sex=' + this.sex + ' underwear=' + this.underwear); // 内衣為外部狀态
};
// 測試
var maleModel = new Model('male');
femaleModel = new Model('female');
for (var i = 1; i <= 50; i++) {
maleModel.underwear = 'underwear' + i;
maleModel.takePhoto();
};
for ( var j = 1; j <= 50; j++ ){
femaleModel.underwear = 'underwear' + j;
femaleModel.takePhoto();
};
10、職責鍊模式:使多個對象都有機會處理請求,進而避免請求的發送者和接收者之間的耦合關系,将這些對象連成一條鍊,并沿着這條鍊傳遞該請求,直到有一個對象處理它為止。
// 職責鍊模式
var order500 = function( orderType, pay, stock ){
if ( orderType === 1 && pay === true ){
console.log( '500 元定金預購,得到 100 優惠券' );
}else{
return 'nextSuccessor'; // 我不知道下一個節點是誰,反正把請求往後面傳遞
}
};
var order200 = function( orderType, pay, stock ){
if ( orderType === 2 && pay === true ){
console.log( '200 元定金預購,得到 50 優惠券' );
}else{
return 'nextSuccessor'; // 我不知道下一個節點是誰,反正把請求往後面傳遞
}
};
var orderNormal = function( orderType, pay, stock ){
if ( stock > 0 ){
console.log( '普通購買,無優惠券' );
}else{
console.log( '手機庫存不足' );
}
};
// Chain.prototype.setNextSuccessor 指定在鍊中的下一個節點
// Chain.prototype.passRequest 傳遞請求給某個節點
var Chain = function (fn) {
this.fn = fn;
this.successor = null;
};
Chain.prototype.setNextSuccessor = function (successor) {
return this.successor = successor;
};
Chain.prototype.passRequest = function () {
var ret = this.fn.apply(this, arguments);
if(ret === 'nextSuccessor') {
return this.successor && this.successor.passRequest.apply(this.successor, arguments);
}
return ret;
};
// 測試
var chainOrder500 = new Chain( order500 );
var chainOrder200 = new Chain( order200 );
var chainOrderNormal = new Chain( orderNormal );
chainOrder500.setNextSuccessor( chainOrder200 );
chainOrder200.setNextSuccessor( chainOrderNormal);
chainOrder500.passRequest( 1, true, 500 );
chainOrder500.passRequest( 2, true, 500 );
chainOrder500.passRequest( 3, true, 500 );
chainOrder500.passRequest( 1, false, 0 );
11、中介者模式:增加一個中介者對象後,所有的相關對象都通過中介者對象來通信,而不是互相引用,是以當一個對象發生改變時,隻需要通知中介者對象即可。
// 中介者模式
var goods = { // 手機庫存
"red|32G": 3,
"red|16G": 0,
"blue|32G": 1,
"blue|16G": 6
};
var mediator = (function(){
var colorSelect = document.getElementById( 'colorSelect' ),
memorySelect = document.getElementById( 'memorySelect' ),
numberInput = document.getElementById( 'numberInput' ),
colorInfo = document.getElementById( 'colorInfo' ),
memoryInfo = document.getElementById( 'memoryInfo' ),
numberInfo = document.getElementById( 'numberInfo' ),
nextBtn = document.getElementById( '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 - 0 ) | 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.oninput = function(){
mediator.changed( this );
};
12、裝飾者模式:在不改變對象自身的基礎上,在程式運作期間給對象動态地添加職責。
裝飾者模式和代理模式:裝飾者模式是實實在在的為對象增加新的職責和行為,而代理做的事情還是跟本體 一樣。
// 用AOP裝飾模式
Function.prototype.before = function(beforefn) {
var _self = this; // 儲存原函數的引用
return function () {
beforefn.apply(this, arguments);
return _self.apply(this, arguments);
}
}
Function.prototype.after = function (afterfn) {
var _self = this;
return function () {
var ret = _self.apply(this, arguments);
afterfn.apply(this, arguments);
return ret;
}
}
13、狀态模式:允許一個對象在其内部狀态改變時改變它的行為,對象看起來似乎修改了它的類。
// 狀态模式
var delegate = function (client, delegation) {
return {
buttonWasPressed: function () {
return delegation.buttonWasPressed.apply(client, arguments);
}
}
};
var FSM = {
off: {
buttonWasPressed: function() {
console.log('關燈');
this.button.innerHTML = '下一次按我是開燈';
this.currState = this.onState;
}
},
on: {
buttonWasPressed: function(){
console.log( '開燈' );
this.button.innerHTML = '下一次按我是關燈';
this.currState = this.offState;
}
}
};
var Light = function () {
this.offState = delegate(this, FSM.off);
this.onState = delegate(this, FSM.on);
this.currState = this.offState;
this.button = null;
};
Light.prototype.init = function () {
var button = document.createElement('button');
self = this;
button.innerHTML = '已關燈';
this.button = document.body.appendChild(button);
this.button.onclick = function () {
self.currState.buttonWasPressed();
}
};
// 測試
var light = new Light();
light.init();
14、擴充卡模式:解決兩個軟體實體間的接口不相容的問題。
// 擴充卡模式
var renderMap = function( map ){
if ( map.show instanceof Function ){
map.show();
}
};
var googleMap = {
show: function(){
console.log( '開始渲染谷歌地圖' );
}
};
var baiduMap = {
display: function(){
console.log( '開始渲染百度地圖' );
}
};
var baiduMapAdapter = {
show: function(){
return baiduMap.display();
}
};
renderMap( googleMap ); // 輸出:開始渲染谷歌地圖
renderMap( baiduMapAdapter ); // 輸出:開始渲染百度地圖