關於W3Cschool定義的設計模式–常用的9種設計模式的介紹

  • 2019 年 10 月 3 日
  • 筆記

一、設計模式

tip:每種設計模式,其實都是為了更高效的,更方便的解決在面對對象編程中所遇到的問題。

什麼是設計模式:
    是一套經過反覆使用、多人知曉的、經過分類的、程式碼設計經驗的總結
 
為什麼使用設計模式:
    為了程式碼的可重用性、讓程式碼更容易被他人理解、保證程式碼的可靠性。設計模式使程式碼的編寫真正的工程化;設計模式是軟體工程的基石脈絡,如同大廈的結構。
 
有哪些設計模式(來自W3Cschool,23種):
    構造器模式,模組化模式,暴露模組模式,單例模式,中介者模式,原型模式,命令模式,外觀模式,工廠模式,Mixin模式,裝飾模式,亨元(Flyweight)模式,MVC模式,MVP模式,MVVM模式,組合模式,適配器模式,外觀模式,觀察者模式,迭代器模式,惰性初始模式,代理模式,建造者模式.
二、工廠模式
  看工廠模式我們首先要看幾個原理。
 (1)new的原理:
  1.創建了一個新的空對象。
  2.將這個對象的__proto__和函數的prototype做連接。
  3.將這個函數中的this改變,指向new新創建的對象。
  4.檢測函數有沒有有返回對象,沒有返回對象,就返回new創建的對象。
 (2)構造函數的原理、特徵和注意事項:
  1.構造函數,構造自定義的函數,會在函數中使用this、找到被構造出來的對象。
  2.隱患:一旦構造函數被直接執行,會錯誤的產生大量的全局變數。
  3.解決隱患:但是解決不了。
  4.自我約束:構造函數不要直接執行。
  5.防止誤操作:行業習慣:為了防止構造函數被直接執行,產生大量的全局變數,一般將需要被構造的函數的首字母大寫,以此標誌構造函數和正常函數的區別。
  工廠模式(構造函數模型)基礎語法:
    function Fn(){          this.name = "root";      }      var f = new Fn()      console.log(f.name)//root

三、單例模式
  單例:單個實例new出來的對象,多次new只有一個對象。
  1、多次new為什麼產生一個對象呢?(new的原理)
  (1).創建了一個新的空對象。

  (2).將這個對象的__proto__和函數的prototype做連接。
  (3).將這個函數中的this改變,指向new新創建的對象。
    (4).檢測函數有沒有有返回對象,沒有返回對象,就返回new創建的對象。
  2.針對默認情況,單獨做出修改。
  3.優化全局空間。
  4.不要覆蓋或修改默認屬性。

單例模式

//單個實例,只有一個對象    //多次創建,返回同一個對象    function fn(){      if(!fn.obj) { //給函數添加一個屬性。 因為函數是全局的,所以該屬性一旦添加一直存在;           fn.obj = {               name : “土土皮皮"           };      }      return fn.obj;  }  var obj1 = new fn();  var obj2 = new fn();  console.log(obj1 == obj2);    //例如我們創建一個資訊提示框,每一次執行toast方法,如果都創建一個新的元素,這樣做太浪費了。  //因此,我們採取單例模式,確保無論調用多少次toast方法,都只創建一個DOM元素。  //我們只控制它的顯示和隱藏,而不是每次創建每次銷毀。    function Toast(){      var div = document.createElement("div");      div.className = "box";      document.body.appendChild(div);      setTimeout(function(){          div.remove();      },1000)  }    obtn.onclick = function(){      var a = new Toast();      var b = new Toast();      console.log(a == b)  }        function Toast(){      if(!Toast.div){          Toast.div = document.createElement("div");          Toast.div.className = "box";          document.body.appendChild(Toast.div);              clearTimeout(Toast.div.timer);              Toast.div.timer = setTimeout(function(){              Toast.div.style.display = "none";          },1000)      }else{          Toast.div.style.display = "block";          clearTimeout(Toast.div.timer);              Toast.div.timer = setTimeout(function(){              Toast.div.style.display = "none";          },1000)      }      return Toast;  }  obtn.onclick = function(){      var a = new Toast();      var b = new Toast();      console.log(a == b);  }

單例模式-彈出框案例

    // 單例模式      function Toast(){          // 第一次執行函數時給構造函數添加屬性為對象          if(!Toast.obj){              Toast.obj = {};              // 對對象進行加工              Toast.obj.dia = document.createElement("dialog");              Toast.obj.dia.innerHTML = "這是一個彈出框";              document.body.appendChild(Toast.obj.dia)          }          // 正常情況下,每次創建元素,都要立即顯示          Toast.obj.dia.style.display = "block";          // 過一定的時間後,隱藏元素          clearTimeout(Toast.obj.t)          Toast.obj.t = setTimeout(() => {              Toast.obj.dia.style.display = "none"          }, 2000);          // 覆蓋this的指向(覆蓋new出來的對象)          return Toast.obj;      }      document.onclick = function(){          var t1 = new Toast()          var t2 = new Toast()          var t3 = new Toast()          var t4 = new Toast()          var t5 = new Toast()  		//這些被new出來的對象,都指向一個對象          console.log(t1 == t2)//true          console.log(t3 == t2)//true          console.log(t5 == t2)//true          console.log(t4 == t2)//true          console.log(t1 == t3)//true          console.log(t3 == t5)//true      }

四、組合模式

  1、組合模式是用來組合對象的,一般應用於頁面,將對象按照一定的規律和關係組合,組成樹狀結構,類似於DOM元素中的樹狀結構,可以完成動態網頁的生成、創建元素和修改樣式。

  2、將對象組合起來之後,可以實現:批量操作。

  3、缺點:節省了操作,消耗了性能。

  tip:組合模式最重要的是組合器。

  

    // 最關鍵的是組合器:      function ImagesStore( id ){          this.children = [];          this.element = document.createElement("div");          this.element.id = id;          document.body.appendChild(this.element)      }      ImagesStore.prototype = {          constructor : ImagesStore,          add:function( child ){              this.children.push( child );              this.element.appendChild( child.getElement() );          },          remove:function( child ){              for( var node, i=0; node = this.getChild(i); i++ ){                  if( node === child ){                      this.children.splice( i, 1 );                      break;                  }              }              this.element.removeChild( child.getElement() );          },          getChild:function( i ){              return this.children[i];          },          show:function(){              this.element.style.border = 'solid 2px black';              for( var node, i=0; node = this.getChild(i); i++ ){                  node.show();              }          },          hide:function(){              for( var node, i=0; node = this.getChild(i); i++ ){                  node.hide();              }              this.element.style.border = 'none';          },          getElement:function(){              return this.element;          }      }        function ImageItem( src ){          this.element = document.createElement("img");          this.element.src = src;          this.element.className = "img-item";      }      ImageItem.prototype = {          constructor:ImageItem,          add:function( child ){              console.log("這是子對象了,沒有添加功能");          },          remove:function( child ){              console.log("這是子對象了,沒有刪除功能");          },          getChild:function( i ){              console.log("這是子對象了,沒有獲取子對象功能");          },          show:function(){              this.element.style.border = 'solid 2px black';          },          hide:function(){              this.element.style.border = 'none';          },          getElement:function(){              return this.element;          }      }          var box = new ImagesStore("box");      var xbox = new ImagesStore("xbox");        var img1 = new ImageItem("https://www.baidu.com/img/bd_logo1.png")      var img2 = new ImageItem("https://www.baidu.com/img/bd_logo1.png")        xbox.add(img1)      xbox.add(img2)        box.add(xbox)      // box.remove(img1)        // img1.show()      box.show()        // img1.add()  

  

    使用組合模式組織起來的對象具有出色的層次結構,每當對頂層組合對象執行一個操作的時候,實際上是在對整個結構進行深度優先的節點搜索。但是這些優點都是用操作的代價換取的,比如每次頂級執行一次show方法,實際的操作就是整個樹形結構的節點都會被遍歷一次。但是組合對象的每個對象之間的耦合非常鬆散,可以簡單的操作處理複雜的結果。
 
    簡單的說,組合模式是講一批子對象組織為樹形結構,一條頂層的命令會在操作樹中所有的對象。提高了程式碼的模組化程度,對於動態的HTML介面具有很強的適用性

五、觀察者模式

  1、觀察者模式又叫發布訂閱者模式:

  (1)發布者:主題對象,一般只有一個。

  (2)接收者:訂閱者,多個,隨時添加和刪除。

  (3)廣播通訊,一個對象發布資訊,多個對象接收資訊,並做出對應處理。

  2、觀察者模式的好處:

  (1)支援簡單的廣播通訊,自動通知所有已經訂閱過的對象。

  (2)頁面載入後目標對象很容易與觀察者存在一種動態關聯,增加了靈活性。

  (3)目標對象與觀察者之間的抽象耦合關係能夠單獨擴展以及重用。

觀察者模式-案例-看孩子還是打麻將

		function child(n){  			this.name = n;  			this.type = function(){  				return Math.random()>0.5? 0 : 1;  			}  		}  		function mather(n,c){  			this.name = n;  			this.child = c;  			this.listen = function(t){  				if(t==0){  					console.log(this.child.name + "哭了,"+this.name+"看孩子")  				}else{  					console.log(this.child.name + "睡了,"+this.name+"打麻將")  				}  			}  		}  		function father(n,c){  			this.name = n;  			this.child = c;  			this.listen = function(t){  				if(t==0){  					console.log(this.child.name + "哭了,"+this.name+"看孩子")  				}else{  					console.log(this.child.name + "睡了,"+this.name+"打麻將")  				}  			}  		}  		var c = new child("大寶");  		var t = c.type();  		var m = new mather('大寶媽',c);  		m.listen(t);  		var f = new father('大寶爸',c);  		f.listen(t);

六、代理模式

  代理模式的應用場景:當我們需要代理一些別人的封裝好的功能或封裝好的組件,像另外一個封裝好的功能或組件發送一些數據的時候,這時候我們沒法從這兩個功能之間去拿數據,這時候我們就可以寫一個中間層或著代理,將這個數據暴露出來,我們就可以再次使用或者多次使用,甚至對這些數據進行修改。
  用一句話總結代理模式:為其他對象提供代理,以控制這個對象的訪問;
    舉個簡單的例子:
    有一個小夥子想要送花給小姑娘,但是不好意思送,於是找到了快遞小哥,快遞小哥幫忙送花;在這裡快遞小哥就是代理!我們就像是那個快遞小哥,等於到程式猿身上要做的就是截獲數據。
    function girl(name){          this.name = name;      }        function boy(girl){          this.girl = girl;          this.sendGift = function(gift){              alert("你好,漂亮的"+this.girl.name+",這是我送你的:"+gift);          }      }        function porxyLitterBrother(girl){          this.girl = girl;          this.send = function(gift){              this.g = gift;              gift = "一個擁抱";              var b = new boy(girl);              b.sendGift(gift);          }          this.init = function(){              console.log(this.g)          }      }      var g = new girl("翠花");      var p = new porxyLitterBrother(g);      p.send("鑽戒")      p.init()

 七、適配器模式

  適配器模式就是將原本不具有某些功能的對象,在使用這些功能時,不出問題,並讓某些不具有特徵的屬性,變得特徵

  demo:

  電子工廠:手機,平板

  手機:打電話,玩遊戲

  平板:玩遊戲

   測試模組只有一個:想能測平板又能測手機,還正確測試,不出問題

		// 應用場景  		// 讓某個不具有明顯特徵的功能,變得有特徵  		function phone(){  			this.name = "phone";  			this.call = function(){  				console.log(this.name + "可以打電話");  			};  			this.game = function(){  				console.log(this.name + "可以打遊戲");  			};  		}  		function pad(){  			this.name = "pad";  			this.game = function(){  				console.log(this.name + "可以打遊戲")  			}  		}  		function text(obj){  			if(obj.call){  				obj.call();  			}else{  				console.log(obj.name+"沒有打電話的功能");  			};  			if(obj.game){  				obj.game();  			}else{  				consloe.log(obj.name+"沒有打遊戲的功能");  			}  		}  		var p1 = new phone();  		text(p1);  		var p2 = new pad();  		text(p2);
          //適配器的意義,多數應用在系統介面使用,也就是別人提供了一個功能,但要求傳入一個A類型的參數
          //而我們手裡的數據是B類型的,如果我們想使用這個功能。那麼有兩種解決辦法:
          //第一,把自己的原程式碼進行修改,讓B類型改為A類型,這是非常蠢的做法。
          //第二,我們把B類型的數據進行一個包裝,讓它看起來符合類型A,這個包裝函數,就是適配器。

 八、抽象工廠模式

  在工廠模式中,將多個實例的相同屬性或方法,再次抽象成一個公共對象,從公共對象上,再次創建出具體的實例。

  demo:

  造車廠:製造汽車

  汽車需要車架子:輪子,引擎。

  我們可以將相同的部分放一起,然後通過添加其他不同的零件,生產不行型號的車。

    var f = (function (){          //抽象工廠模式主要就是這個公共對象,模具對象          function car(wheel,engine){ //內部配置函數,可以提供配置功能。              this.wheel = wheel;              this.engine = engine;          }          return function(wheel , engine){ // 構造器, 負責創建對象。              return new car(wheel,engine); // 這是對外提供的介面,負責和外部需求連接。           }      })();        var car1 = f("15","V8");      var car2 = f("13","V10");    //每次執行都會有這個公共對象,執行這個公共對象,獲得一個新的對象      console.log(car1);//一個新的car對象      console.log(car2);//一個新的car對象        console.log(car1 == car2);//false
  //這種模式,就是所謂的抽象工廠模式。 

 九、策略模式

  策略:計劃,規劃,預製要做的事情,不同的情況訂製不同的計劃。

    function fn(n){          if(n < 10 || n.length < 1){              return "0"+n          }else{              return n;          }      }      var num = fn(4);      console.log(num)//04

 總結:這就是我們平時常用的九種設計模式,每種設計模式其實都是見名識義,很多種設計模式我們也只會在寫一些大型的項目的時候我們才會使用,每一種設計模式我們都需要根據當前的實際需求,來判斷我們該使用哪種設計模式,使我們的程式碼解構更強。