1. 程式人生 > >HTML前端各種常見的設計模式

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

不清晰 splice nav list dmi 常見 語言 相同屬性 -s

前言

HTML設計模式定義:在面向對象軟件設計過程中 針對特定問題的簡潔而優雅的解決方案。在不同的編程語言中,對設計模式的實現其實是可能會有區別的。

  • 單例模式
  • 觀察者模式
  • 工廠模式
  • 命令模式
  • 職責鏈模式

1,單例模式

定義:是保證一個類只有一個實例,並且提供一個訪問它的全局訪問點

需求:一些對象我們往往只需要一個,比如線程池、全局緩存、瀏覽器中的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
技術分享圖片

實踐栗子

技術分享圖片
(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 ); // 輸出:手機庫存不足
技術分享圖片

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