JavaScript設計模式之建立型設計模式

Neal_yang發表於2019-01-02

此係列總結與《JavaScript設計模式》,總共分為建立型設計模式、結構型設計模式、行為型設計模式、技巧型設計模式和架構性設計模式五大類。

github原文地址:YOU-SHOULD-KNOW-JS

JavaScript設計模式之簡單工廠設計模式

作為建立性設計模式中的第一個介紹的,這個模式必然比較簡單,所以不做過多介紹

使用場景

簡單工廠模式的概念就是建立物件,不僅如此,簡單工廠模式還可以用來建立相似的物件。

舉個例子來說,如果你想建立一些書,那麼這些書都有一些相似的地方,比如目錄、頁碼等。也有很多不同的地方,
如書名、出版時間、書的型別等,對於建立的物件相似的屬性當然好處理,對於不同的屬性就要有針對的修改處理了。

程式碼

//工廠模式
function createBook(name,time,type) {
  //建立一個物件,並且對物件擴充屬性和方法
  var o = new Object();
  o.name = name;
  o.time = time;
  o.type = type;
  o.getName = function() {
    console.log(this.name);
  }
  
  //將物件返回
  return o;
}

var book1 =  createBook(`js book`,`2017/11/16`,`JS`);
var book2 =  createBook(`css book`,`2017/11/13`,`CSS`);

book1.getName();
book2.getName();
複製程式碼
var Basketball = function() {
  this.info=`美國籃球`
}
Basketball.prototype = {
  constructor:Basketball,
  getMember:function() {
    console.log(`每隊需要5個成員`);
  },
  getBallSize:function() {
    console.log(`這個籃球還是很大的`);
  }
}
var Football = function() {
  this.info = `這是足球`
}
Football.prototype = {
    constructor:Football,
    getMember:function() {
      console.log(`足球每隊需要十一個人`)
    },
    getBallSize:function() {
      console.log(`足球我不喜歡`);
    }
}
var Tennis = function() {
  this.info = `網球貌似現在還是蠻流行的`;
}
Tennis.prototype = {
    constructor:Tennis,
    getMember:function() {
      console.log(`一對一,二對二咯`);
    },
    getBallSize:function() {
      console.log(`網球還是比較小的`);
    }
}

//球類工廠
var sportsFactory = function(name) {
  switch (name){
      case `NBA`:
          return new Basketball();
          break;
      case `wordCup`:
          return new Football();
          break;
      default :
          return new Tennis();
  }
}
複製程式碼

說明

這種簡單工廠模式非常的像寄生式繼承,只不過這裡o沒有繼承任何物件和類。

簡單工廠的用途就是建立物件,或者建立相似的物件。比較簡單,比較常規,這裡不再贅述

JavaScript設計模式之工廠方法模式

學習一個東西,一定要明白這個東西的概念是什麼,這個東西被提出來的目的是什麼

前言

在之前,我們介紹過簡單工廠設計模式,簡單工廠設計模式存在唯一的工廠類,它的優點是所有產品類的例項化集中管理,便於理解。當產品數量較少,且不會經常發生變化時,我們當然可以直接使用簡單工廠模式,但是有的時候,需求是在時刻變化的,產品類也可能隨之增加,如果使用簡單工廠模式,就避免不了去修改工廠類的程式碼。要解決這個問題,就得使用今天所講的,工廠方法模式。

工廠方法模式本意是將實際建立物件的工作推遲到子類當中。這樣核心類就成為了抽象類。

基本概念

工廠方法模式:不再有一個唯一的工廠類就建立產品,而是將不同的產品交給對應的工廠子類去實現。每個產品由負責生產的子工廠來創造。如果新增新的產品,需要做的是新增新的子工廠和產品,而不需要修改其他的工廠程式碼。

工廠方法模式主要有三種類組成:

  • 抽象工廠類:負責定義建立產品的公共介面
  • 產品子工廠:繼承抽象工廠類,實現抽象工廠類提供的介面
  • 每一種產品各自的產品類

安全模式類

安全模式類就是可以遮蔽對類的錯誤使用而造成的後果。說白了,就是在建構函式開始時先判斷當前物件this指向是不是類。

var Demo = function() {
  if(!(this instanceof Demo)){
      return new Demo();
  }
}
Demo.prototype.show = function() {
  console.log(`show`)
}

var d = Demo();
d.show();
複製程式碼

工廠方法模式

簡單工廠模式僅僅適用於建立比較少的物件,如果需要建立多個類,並且會經常修改,像我們之前說的簡單工廠的方法就不是很實用了,因為如果我要多新增一個類,就需要修改兩個地方,所以這裡我們採用工廠方法模式

var Factory = function(type,content) {
  if(this instanceof Factory){
      var temp = new this[type](content);
  }else{
      return new Factory(type,content);
  }
}
//在工廠原型中設定建立所有型別資料物件的基類
Factory.prototype = {
    constructor:Factory,
    Java:function(content) {
      //...
    },
    JavaScript:function(content) {
      //...
    },
    UI:function(content) {
      this.content = content;
      (function(content) {
        var div =  document.createElement(`div`);
        div.innerHTML = content;
        div.style.border = `1px solid red`;
        document.getElementById(content).appendChild(div);
        
      })(content)
    }
}
複製程式碼

如上,我們就可以建立多個類了

var data = [
    {type:`JavaScript`,content:`Javascript還是很重要的`},
    {type:`Java`,content:`Java培訓哪家強`},
    {type:`UI`,content:`UI...`}
];

for(var i = 0,length=data.length;i++;i<data.length){
    Factory(data[i].type,data[i].content);
}
複製程式碼

JavaScript設計模式之抽象工廠模式

學習一個東西,一定要明白這個東西的概念是什麼,這個東西被提出來的目的是什麼

概念

通過對類的工廠抽象使其業務用於對產品類簇的建立,而不負責建立某一類產品的例項。也就是說我們要建立一個抽象類。這也是物件導向的開發語言中一種很常見的開發模式。

應用場景

var Car = function() {};
Car.prototype.getPrice = function() {
  return new Error(`抽象方法不能呼叫,需自行實現`);
};
Car.prototype.getSpeed = function() {
  return new Error(`抽象方法不能呼叫,需自行實現`);
 }
複製程式碼

由於JavaScript在沒有abstract的具體實現,所以我們需要如上手動實現,也即是在建立這個類的時候,要求使用這些方法,我們需要手動去重寫它,而不能繼承使用。因為
在大型的應用中,總有一些子類去繼承一些父類,這些父類經常會定義一些必要的方法,但是不會具體的去實現,會去要求子類自行實現。如果子類沒有重寫這些方法而去呼叫他,就會報錯。

程式碼演示

// 抽象工廠方法
var VehicleFactory = function(subType,superType) {
  //判斷抽象工廠中是否有該抽象類
  if(typeof VehicleFactory[superType] === `function`){
      //快取類
      function F() {};
      //繼承父類屬性和方法
      F.prototype = new VehicleFactory[superType]();
      //將子類的constructor指向子類
      subType.constructor = subType;
      //子類原型繼承父類
      subType.prototype = new F();
  }else{
      return 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.Trunk = function() {
  this.type = `Trunk`;
}
VehicleFactory.Trunk.prototype = {
    getPrice:function() {
      return new Error(`抽象方法不能呼叫`);
    },
    getSpeed:function() {
      return new Error(`抽象方法不能呼叫`);
    }
};
//子類
var BMW = function(price,speed) {
  this.price = price;
  this.speed = speed;
}
VehicleFactory(BMW,`Car`);
BMW.prototype.getPrice = function() {
  return this.price;
}
BMW.prototype.getSpeed = function() {
  return this.speed;
}

//...

var three = new BMW(`35w`,`200`);
three.getSpeed();
console.log(three.getPrice())
複製程式碼

所以從上,我們可以看出,抽象工廠其實是實現子類繼承父類的方法,在這個方法裡,我們需要傳遞子類以及需要被繼承的父類的名稱,並且在抽象工廠方法中,又增加了一次對抽象類存在性的一次判斷,然後通過寄生式繼承,在繼承中我們是通過new關鍵字複製了父類的一個例項,因為我們不僅僅需要繼承父類原型上的方法,還需要繼承父類的屬性。所以通過new關鍵字將父類的建構函式執行一遍來複制父類建構函式中的屬性和方法。

通過抽象工廠,我們就能知道每一個子類到底是哪一種類別了。

同時注意,抽象類中定義的方法這是顯示定義一些功能,但是沒有具體的實現,而一個物件是應該具備一套完整的功能的。所以用抽象類建立的物件當然也是抽象的。所以我們還不能直接用它來建立類的例項。

JavaScript設計模式之建造者模式

學習一個東西,一定要明白這個東西的概念是什麼,這個東西被提出來的目的是什麼

概念

建造者模式:將一個複雜物件的構建層和表示層相分離,同樣的構建過程可以採用不同的表示。

應用場景

工廠模式主要是用來建立物件的例項(簡單工廠模式,工廠方法模式)或者是類簇(抽象工廠模式),關心的是最終的產出是什麼,所以工廠模式我們得到的是物件的例項或者物件的類簇。然而建造者模式在建立物件的過程中則更為複雜一些。雖然目的也是為了建立物件,但是更關心的建立物件的整個過程或者說是每一個細節。

比如建立一個人,我們建立的結果不僅僅是得到一個人的例項,還要關注建立人的時候,這個人是男是女,穿什麼衣服帶什麼帽子等等。

程式碼演示

var Human = function(param) {
  this.skill = param && param.skill || `保密`;
  this.hobby = param && param.hobby || `保密`;
}
Human.prototype = {
    constructor:Human,
    getSill:function() {
      return this.skill;
    },
    getHobby:function() {
      return this.hobby;
    }
}
var Name = function(name) {
  var that = this;
  (function(name,that) {
    this.wholeName = name;
    if(name.indexOf(` `)>-1){
        that.firstName = name.slice(0,name.indexOf(` `));
        that.secondName = name.slice(name.indexOf(` `));
    }
  })(name,that)
}
var Work = function(work) {
  var that = this;
  (function(work,that) {
    switch (work){
        case `code` :
            that.work = `工程師`;
            that.wordDesc = `程式碼是我快樂`;
            break;
        case `UE` :
            that.work = `設計師`;
            that.wordDesc = `設計更似藝術`;
            break;
        default :
            that.work = work;
            that.wordDesc = `對不起,我們還不清楚你所選擇職位的相關描述`;
    }
  })(work,that);
}

//更換期望職位以及描述
Work.prototype.changeWork = function(work) {
  this.work = work;
}

Work.prototype.changeDesc = function(desc) {
  this.wordDesc = desc;
}

//建立一個應聘者
var Person = function(name,work) {
  var _person = new Human();
  _person.name = new Name(name);
  _person.work = new Work(work);
  return _person;
}

var person = new Person(`Neal yang`,`code`);
console.log(person.skill);
console.log(person.hobby);
console.info(person.work);
person.work.changeDesc(`一擼程式碼就瘋狂`);
console.info(person.work);
複製程式碼

JavaScript設計模式之原型模式

學習一個東西,一定要明白這個東西的概念是什麼,這個東西被提出來的目的是什麼

概念

原型模式:用原型例項指向建立物件的類,適用於建立新的物件的類共享原型物件的屬性和方法。

這種繼承是一種基於對屬性和方法的共享而不是複製。

應用場景

在建立的類中,存在基類,起定義的方法和屬效能夠被子類所繼承和使用。

原型模式就是將可複用的、可共享的、消耗大的從基類中提取出來然後放到原型中,然後子類通過組合繼承或者寄生組合式繼承將方法和屬性繼承下來。子類中對於那些需要重寫的方法進行重寫,這樣,子類
建立的物件既有子類的屬性和方法也共享著基類的屬性和方法。

程式碼演示

拿網頁中輪播圖距離。大概的樣子就是輪播圖,但是有的是漸變效果,有的是滾動,有的帶有箭頭。。。所以這裡我們可以建立一個基類,輪播圖,然後再根據不同的需求再去修改。

//圖片輪播圖類
var LoopImages =  function(imgArr,container) {
  this.imageArr = imgArr;//輪播圖片陣列
  this.container = container;//輪播圖片容器
  this.createImage = function() {
    
  };//建立輪播圖
  this.changeImage = function() {
    
  }//輪播圖切換
}
//上下切換
var SlideLoopImage = function(imgArr,container) {
    //建構函式繼承圖片輪播類
  LoopImages.call(this,imgArr,container);
  
  //重寫繼承的圖片切換方法
  this.changeImage = function() {
    console.log(`上下切換的方式`)
  }
}
//漸隱切換
var FadeLoopImg =  function(imgArr,container,arrow) {
  LoopImages.call(this,imgArr,container);
    //切換箭頭私有變數
    this.arrow = arrow;
  this.changeImage = function() {
    console.log(`漸隱的切換方式`);
  }
}

//例項化一個
var fadeImg = new FadeLoopImg([`01.jpg`,`02.jpg`,`03.jpg`],`slide`,[`left.jpg`,`right.jpg`]);
複製程式碼

但是如上的寫法 ,其實還有一種更加優化的方法。首先看基類,作為基類是要被子類繼承的,那麼此時將屬性和方法都寫到基類的建構函式裡會有一些問題。
比如每一次建立子類繼承都要建立一次父類,如果父類的建構函式的建立過程中存在很多耗時較長的邏輯,這樣的話效能消耗還是蠻大的。為了提高效能,我們可以使用共享機制。

對於每一次建立的一些簡單而又差異化的屬性我們可以放到建構函式中,而把一些消耗資源比較大的方法放到基類的原型中。這樣避免很多不必要的消耗。

var LoopImages = function(imgArr,container) {
  this.imageArr = imgArr;
  this.container = container;
}
LoopImages.prototype = {
    crateImage : function() {
      console.log(`建立輪播圖方法`);
    },
    changeImage:function() {
      console.log(`圖片切換方法`);
    }
}
//上下切換
var SlideloopImg = function(imgArr,container) {
  LoopImages.call(this,imgArr,container);
}
SlideloopImg.prototype = new LoopImages();

SlideloopImg.prototype.changeImage = function() {
  console.log(`上下切換的方式`);
}

var FadeLoopImg = function(imgArr,container,arrow) {
  this.arrow = arrow;
  LoopImages.call(this,imgArr,container);
}
FadeLoopImg.prototype = new LoopImages();

FadeLoopImg.prototype.changeImage = function() {
  console.log(`漸隱切換方式`);
}
複製程式碼

JavaScript設計模式之單例模式

學習一個東西,一定要明白這個東西的概念是什麼,這個東西被提出來的目的是什麼

概念

單例模式又稱為單體模式,其實就是隻允許例項化一個物件,有時我們也可以用一個物件類規劃名稱空間,僅僅有條的管理物件的屬性和方法.

單例模式比較常規常見,比較簡單,直接看程式碼

程式碼演示

//名稱空間管理
var Neal = {
    g:function(id) {
      return document.getElementById(id)
    },
    css:function(id,key,value) {
      this.g(id).style[key] = value;
    },
    //...
}
//模組分明
var A = {
    Util:{
        util_method1:function() {
          
        },
        util_method2:function() {
          
        }
    },
    Tool:{
        tool_method1:function(){},
        tool_method2:function(){},
    }
    //...
}
//惰性單例
var LazySingle = (function() {
  //單例例項引用
  var _instance = null;
  //單例
  function Single() {
    return{
        publicMethod:function() {
          
        },
        publicProperty:`1.0`
    }
  }
  //獲取單例介面
  return function() {
    if(!_instance){
        _instance = Single();
    }
    //返回單例
    return _instance;
  }
})()
複製程式碼

相關文章