前言javascript
HTML設計模式定義:在面向對象軟件設計過程當中 針對特定問題的簡潔而優雅的解決方案。在不一樣的編程語言中,對設計模式的實現實際上是可能會有區別的。html
- 單例模式
- 觀察者模式
- 工廠模式
- 命令模式
- 職責鏈模式
1,單例模式 java
定義:是保證一個類只有一個實例,而且提供一個訪問它的全局訪問點。編程
需求:一些對象咱們每每只須要一個,好比線程池、全局緩存、瀏覽器中的window對象、登陸浮窗等。設計模式
實現:用一個變量標識當前是否已經爲某個類建立過對象,若是是,則在下一次獲取這個類的實例時,直接返回以前建立的對象。瀏覽器
優勢:緩存
- 能夠用來劃分命名空間,減小全局變量的數量
- 能夠被實例化,且實例化一次,再次實例化生成的也是第一個實例
基礎栗子:安全
// 單例模式 var Singleton = function(name){ this.name = name; this.instance = null; }; Singleton.prototype.getName = function(){ return this.name; }; // 獲取實例對象 Singleton.getInstance = function(name) { if(!this.instance) { this.instance = new Singleton(name); } return this.instance; }; // 測試單例模式的實例 var a = Singleton.getInstance("aa"); var b = Singleton.getInstance("bb"); console.log(a===b) // true
實踐栗子:app
(function () { //管理單例的邏輯代碼,若是沒有數據則建立,有數據則返回 var getSingle = function(fn){ //參數爲建立對象的方法 var result; return function(){ //判斷是Null或賦值 return result || (result = fn.apply(this,arguments)); }; }; //建立登陸窗口方法 var createLoginLayer = function(){ var div = document.createElement('div'); div.innerHTML = '我是登陸浮窗'; div.style.display = 'none'; document.body.appendChild(div); return div; }; //單例方法 var createSingleLoginLayer = getSingle(createLoginLayer); //使用惰性單例,進行建立 document.getElementById('loginBtn').onclick = function(){ var loginLayer = createSingleLoginLayer(); loginLayer.style.display = 'block'; }; })()
2,觀察者模式編程語言
定義:對象間的一種一對多的依賴關係。
需求:當一個對象的狀態發生變化時,全部依賴於他的對象都將獲得通知。
優勢:時間上的解耦,對象之間的解耦。
實現:
- 首先,指定好誰充當發佈者;
- 而後,給發佈者添加一個緩存列表,用於存放回調函數以便通知訂閱者;
- 最後,發佈消息的時候,發佈者會遍歷這個緩存列表,依次觸發裏面存放的訂閱者回調函數。
基礎栗子:
var salesOffices = {}; // 定義售樓處 salesOffices.clientList = []; // 緩存列表,存放訂閱者的回調函數 salesOffices.listen = function( fn ){ // 增長訂閱者 this.clientList.push( fn ); // 訂閱的消息添加進緩存列表 }; salesOffices.trigger = function(){ // 發佈消息 for( var i = 0, fn; fn = this.clientList[ i++ ]; ){ fn.apply( this, arguments ); // arguments 是發佈消息時帶上的參數 } }; //調用 salesOffices.listen( function( price, squareMeter ){//訂閱消息 console.log( '價格= ' + price ); console.log( 'squareMeter= ' + squareMeter ); }); salesOffices.trigger( 2000000, 88 ); // 輸出:200 萬,88 平方米
實踐栗子:登陸頁面登陸後,會須要刷新各個模塊的信息(頭像、nav)這類。
var ObserverEvent = (function () { var clientList = [], listen, trigger, remove; listen = function (key, fn) { if (!clientList[key]) { clientList[key] = []; } clientList[key].push(fn); }; trigger = function () { var key = Array.prototype.shift.call(arguments), fns = clientList[key]; if (!fns || fns.length === 0) { return false; } for (var i = 0, fn; fn = fns[i++];) { fn.apply(this, arguments); } }; remove = function (key, fn) { var fns = clientList[key]; if (!fns) { return false; } if (!fn) { fns && (fns.length = 0); } else { for (var l = fns.length - 1; l >= 0; l--) { var _fn = fns[l]; if (_fn === fn) { fns.splice(l, 1); } } } }; return { listen:listen, trigger:trigger, remove:remove } })(); ObserverEvent.listen('squareMeter88', fn1 = function (price) { console.log('價格=' + price); }); ObserverEvent.listen('squareMeter100', function (price) { console.log('價格=' + price); }); ObserverEvent.trigger('squareMeter88', 200000); //刷新模塊信息 var header = (function () { ObserverEvent.listen('loginSucc', function (data) { header.setAvatar(data.avatar); }); return { setAvatar: function (data) { console.log(data + "設置header成功"); } } })(); var nav = (function () { ObserverEvent.listen('loginSucc', function (data) { nav.setAvatar(data.avatar) }); return { setAvatar: function (data) { console.log(data + '設置nav成功'); } } })(); var data = {}; data.avatar = "參數"; ObserverEvent.trigger('loginSucc', data);
3,工廠模式:
定義:將其成員對象的實例化推遲到子類來實現的類。
需求:建立對象的流程賦值的時候,好比依賴於不少設置文件等 ;處理大量具備相同屬性的小對象;注:不能濫用
優勢:不暴露建立對象的具體邏輯,而是將將邏輯封裝在一個函數中。
分類:簡單工廠,工廠方法和抽象工廠。
實現:
3.1 簡單工廠模式 (建立單一對象,須要的類比較少)
let UserFactory = function (role) { function SuperAdmin() { this.name = "超級管理員", this.viewPage = ['首頁', '通信錄', '發現頁', '應用數據', '權限管理'] } function Admin() { this.name = "管理員", this.viewPage = ['首頁', '通信錄', '發現頁', '應用數據'] } function NormalUser() { this.name = '普通用戶', this.viewPage = ['首頁', '通信錄', '發現頁'] } switch (role) { case 'superAdmin': return new SuperAdmin(); break; case 'admin': return new Admin(); break; case 'user': return new NormalUser(); break; default: throw new Error('參數錯誤, 可選參數:superAdmin、admin、user'); } }
3.2 工廠方法模式 (建立多類對象,須要的類比較多)
爲方便後續新增類方便,只需改一處代碼,封裝了工廠方法而已。而且把類都放在工廠類原型中實現。
//安全模式建立的工廠方法函數 let UserFactory = function(role) { if(this instanceof UserFactory) { var s = new this[role](); return s; } else { return new UserFactory(role); } } //工廠方法函數的原型中設置全部對象的構造函數 UserFactory.prototype = { SuperAdmin: function() { this.name = "超級管理員", this.viewPage = ['首頁', '通信錄', '發現頁', '應用數據', '權限管理'] }, Admin: function() { this.name = "管理員", this.viewPage = ['首頁', '通信錄', '發現頁', '應用數據'] }, NormalUser: function() { this.name = '普通用戶', this.viewPage = ['首頁', '通信錄', '發現頁'] } } //調用 let superAdmin = UserFactory('SuperAdmin'); let admin = UserFactory('Admin') let normalUser = UserFactory('NormalUser')
3.3 抽象工廠模式 (建立父類,子類繼承父類,具體實如今子類)
抽象工廠實際上是實現子類繼承父類的方法,只是一個方法。
抽象工廠模式通常用在多人協做的超大型項目中,而且嚴格的要求項目以面向對象的思想進行完成。
// 抽象工廠方法 var VehicleFatory = function(subType, superType) { // 判斷抽象工廠中是否有該抽象類 if(typeof VehicleFactory[superType] === 'function') { // 緩存類 function F() {}; // 繼承父類屬性和方法 F.prototype = new VehicleFactory[superType] (); // 將子類constructor 指向子類 subType.constructor = subType; // 子類原型繼承'父類' subType.prototype = new F(); } else { // 不存在該抽象類拋出錯誤 throw new Error('未建立該抽象類'); } }; // 小汽車抽象類 VehicleFactory.Car = function() { this.type = 'car'; }; VehicleFactory.Car.prototype = { getPrice: function() { return new Error('抽象方法不能調用'); }, getSpeed: function() { return new Error('抽象方法不能調用'); } }; // 公交車抽象類 VehicleFactory.Bus = function() { this.type = 'bus'; }; VehicleFactory.Bus.prototype = { getPrice: function() { return new Error('抽象方法不能調用'); }, getSpeed: function() { return new Error('抽象方法不能調用'); } }; // 貨車抽象類 VehicleFactory.Truck = function() { this.type = 'truck'; }; VehicleFactory.Truck.prototype = { getPrice: function() { return new Error('抽象方法不能調用'); }, getSpeed: function() { return new Error('抽象方法不能調用'); } }; // 建立產品子類繼承相應的產品簇抽象類 // 寶馬汽車子類 var BMW = function(price, speed) { this.price = price; this.speed = speed; } //抽象工廠實現對Car抽象類的繼承 VehicleFactory(BMW, 'Car'); BMW.prototype.getPrice = function() { return this.price }; BMW.prototype.getSpeed = function() { return this.speed }; // 公交車... // 貨車...
4,命令模式:
定義:用來對方法調用進行參數化處理和傳送,通過這樣處理過的方法調用能夠在任何須要的時候執行。
需求:有時候須要向某些對象發送請求,可是並不知道請求的接收者是誰,也不知道被請求的操做是什麼,此時但願用一種鬆耦合的方式來設計軟件,使得請求發送者和請求接收者可以消除彼此之間的耦合關係。
實現:將函數的調用、請求和操做封裝成一個單一的對象。
1 var setCommand = function(button,func) { 2 button.onclick = function(){ 3 func(); 4 } 5 }; 6 var MenuBar = { 7 refersh: function(){ 8 alert("刷新菜單界面"); 9 } 10 }; 11 var SubMenu = { 12 add: function(){ 13 alert("增長菜單"); 14 } 15 }; 16 // 刷新菜單 17 var RefreshMenuBarCommand = function(receiver) { 18 return function(){ 19 receiver.refersh(); 20 }; 21 }; 22 // 增長菜單 23 var AddSubMenuCommand = function(receiver) { 24 return function(){ 25 receiver.add(); 26 }; 27 }; 28 var refershMenuBarCommand = RefreshMenuBarCommand(MenuBar); 29 // 增長菜單 30 var addSubMenuCommand = AddSubMenuCommand(SubMenu); 31 setCommand(b1,refershMenuBarCommand); 32 33 setCommand(b2,addSubMenuCommand);
5,職責鏈模式:
定義:使多個對象都有機會處理請求,從而避免請求的發送者和接收者之間的耦合關係,將這些對象連成一條鏈,並沿着這條鏈傳遞該請求,直到有一個對象處理它爲止。(大函數分割成一個個小函數,清晰,各司其職)
需求:代碼不清晰,可讀性差,拆分函數。
實現:
//----------------------改造前---------------
var order = function( orderType, pay, stock ){ if ( orderType === 1 ){ // 500 元定金購買模式 if ( pay === true ){ // 已支付定金 console.log( '500 元定金預購, 獲得 100 優惠券' ); }else{ // 未支付定金,降級到普通購買模式 if ( stock > 0 ){ // 用於普通購買的手機還有庫存 console.log( '普通購買, 無優惠券' ); }else{ console.log( '手機庫存不足' ); } } } else if ( orderType === 2 ){ // 200 元定金購買模式 if ( pay === true ){ console.log( '200 元定金預購, 獲得 50 優惠券' ); }else{ if ( stock > 0 ){ console.log( '普通購買, 無優惠券' ); }else{ console.log( '手機庫存不足' ); } } } else if ( orderType === 3 ){ if ( stock > 0 ){ console.log( '普通購買, 無優惠券' ); }else{ console.log( '手機庫存不足' ); } } }; order( 1 , true, 500); // 輸出: 500 元定金預購, 獲得 100 優惠券
//--------------------- 改造後----------------------------
// 500 元訂單 var order500 = function( orderType, pay, stock ){ if ( orderType === 1 && pay === true ){ console.log( '500 元定金預購, 獲得 100 優惠券' ); }else{ order200( orderType, pay, stock ); // 將請求傳遞給 200 元訂單 } }; // 200 元訂單 var order200 = function( orderType, pay, stock ){ if ( orderType === 2 && pay === true ){ console.log( '200 元定金預購, 獲得 50 優惠券' ); }else{ orderNormal( orderType, pay, stock ); // 將請求傳遞給普通訂單 } }; // 普通購買訂單 var orderNormal = function( orderType, pay, stock ){ if ( stock > 0 ){ console.log( '普通購買, 無優惠券' ); }else{ console.log( '手機庫存不足' ); } }; // 測試結果: order500( 1 , true, 500); // 輸出:500 元定金預購, 獲得 100 優惠券 order500( 1, false, 500 ); // 輸出:普通購買, 無優惠券 order500( 2, true, 500 ); // 輸出:200 元定金預購, 獲得 500 優惠券 order500( 3, false, 500 ); // 輸出:普通購買, 無優惠券 order500( 3, false, 0 ); // 輸出:手機庫存不足