HTML前端各類常見的設計模式

前言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,觀察者模式編程語言

     定義:對象間的一種一對多的依賴關係。

      需求:當一個對象的狀態發生變化時,全部依賴於他的對象都將獲得通知。

      優勢:時間上的解耦,對象之間的解耦。

      實現:

  1. 首先,指定好誰充當發佈者;    
  2. 而後,給發佈者添加一個緩存列表,用於存放回調函數以便通知訂閱者; 
  3. 最後,發佈消息的時候,發佈者會遍歷這個緩存列表,依次觸發裏面存放的訂閱者回調函數。

     基礎栗子:

複製代碼
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 ); // 輸出:手機庫存不足
複製代碼
相關文章
相關標籤/搜索