子慕談設計模式系列(一)


前言:

設計模式不容易用文字描述清楚,而過多的代碼,看起來也讓人摸不到頭腦,加上詞語或者文字描述的抽象感,很容易讓人看了無數設計模式的文章,也仍然理解不了。  所以我一直打算寫此系列博客,首先我會從大量文章里去理解這些設計模式,最后我用自己的語言組織轉化為博客,希望用更少的代碼,更容易理解的文字,來聊一聊這些設計模式。  我所理解、所描述的每一個設計模式也可能有些是錯誤的,甚至也不一定有非常深刻的理解,所以希望有人指出,我可以更改博客內容。  因為我是前端,所以設計模式的代碼以前端代碼和視角為主。  此博客內容對每一種模式並不會寫得非常深入,也許能為讀者打通一些認知,如果看了此系列博客,再去看其他更深入的博客,可能是一種比較好的方式。

 

單例模式

單例是保證一個類只創建一個實例,實例不存在新建一個實例,實例存在返回已經存在的實例。  單例模式很好理解,使用情況也很多,比如我最近做的ng4的項目,會定義一些service(ng的service都是單例),service里面存放的數據供全局使用,所有組件共享這個service。  再說一個應用實例: 全局toast框,我們只需要每次調用同一個實例改變toast框里的文本,並控制其隱藏顯示。

 

工廠模式

下面是書中工廠模式的例子,關於它的實現和優劣我就不說了,這里只是以個人的理解來說為什么它叫工廠模式。  創建對象的時候,都會先新建一個新的原生對象,再對它進行屬性賦值,最后返回一個成品對象。所有屬性和方法都在它內部,都是它唯一擁有的。那么傳統工廠生產出來的電視手機等這些硬件,他們就是這種模式,他們的在出廠前就會在產品內部定義好所有的組件,最后加工為成品。所以我們就這樣來理解工廠模式的命名吧。

 

function createPerson(name, age, job){
var o = new Object();
o.name = name;
o.age = age;
o.job = job;
o.sayName = function(){
alert(this.name);
};
return o;
}
var person1 = createPerson("Nicholas", 29, "Software Engineer");
var person2 = createPerson("Greg", 27, "Doctor");

 

 

建造者模式

使用多個簡單的對象一步一步構建成一個復雜的對象。它使組裝過程和每個部件的開發分離開來。

直接舉個例子: 假如我們要設計一個h5飛機游戲,那么我們設計一款飛機的前端代碼,把飛機先簡單拆分為如下兩個部件: 機體、子彈。每個部件定義一個對象,然后設置它們的參數(比如飛機圖片地址,子彈設置傷害100的威力值和子彈圖片地址),最后通過組裝代碼邏輯來組裝成一架完整的游戲飛機。 那么飛機是一個復雜對象,這兩個部件就是更單純更簡單的對象。來點代碼示例:

function plane(){
  this.buildBodyModule();
this.buildBulletModule(); } plane.prototype = { buildBodyModule: function(){ this.body = {
imgUrl:'xxx.png',
destoryImgUrl: 'xx2.png'
} },
buildBulletModule: function(){ this.bullet = {
imgUrl: 'xx3.png',
power: 100
} } }

當需求變動的時候,我們只需修改對應的單個部件,甚至可以隨時在移除或者添加其它部件。 所以這樣我們就大致能理解為什么它叫建造者模式了,現實生活中,一個復雜工程,就比如汽車、建築、飛機等,他們都是由不同的精細設計的部件通過合理的組裝才生產出成品的。  建造者模式和裝飾者模式看起來實際有點類似,下面就說裝飾者模式。

 

裝飾者模式

裝飾模式指的是在不必改變原類文件和使用繼承的情況下,動態地擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。  其實裝飾者模式就是通過一個特定的方法,給一個實例對象添加特定的功能。比如一個基礎汽車類,有引擎、輪胎和車架,代碼如下:

 

function BaseCar(){}
BaseCar.prototype = {
  engine: e300,
  tyre: t200,
  frame: f10
}

 

我們在車的基礎上,貼一些裝飾紙,以下代碼就是最基礎的裝飾者模式的使用。

var car = new BaseCar();
setStyle(car);

setStyle(carObj){
  carObj.style = '達康書記玻璃貼紙';
}

 所謂裝飾者模式,就是通過一個外部的方法,來包裝一個對象,並且擴展它。

 

 

裝飾者模式 VS 建造者模式

 實際裝飾者模式和建造者模式,都有類似分步的方式去建立一個對象。而建造者模式是在類的內部,一步步建造對象,很強調整體性和邏輯性,當然我上面的例子並沒有體現邏輯性之說,如果我們把飛機的機體再拆分一下,我們可能需要先建立機體的機艙(圖片),再根據機艙位置,放置或者說定位側翼和尾翼圖片位置,那么這就是所謂邏輯性了。  而裝飾者模式,是傳入對象到外部函數,通過函數體內部對對象進行擴展。其實通過外部函數,也能達到邏輯性的要求,通過外部函數的多次調用,讓對象添加和迭代功能。但是如果所有的組裝過程都是通過外部函數進行,在對象屬性和子對象的引用上可能會讓代碼變得非常復雜,甚至可能需要函數里返回對象,再用另一個函數包裹這類的函數嵌套,整體的邏輯從代碼層面看來也會變得復雜和難以理解。  對於裝飾者模式的使用,還是應該如它的名字一樣,就應該是為一個基礎功能完整的對象添加一些額外的擴展。  在面向對象編程的程序里,建造者模式和裝飾者模式應該是常會結合在一起使用,把上面的汽車例子,改編成一個更像這兩模式結合的代碼:

function BaseCar(){
    this.buildFrame();
    this.buildEngine();
    this.buildTyre();
}
BaseCar.prototype = {
  buildFrame: function(){
    this.frame = 'f10';
    //todo sth
  },
  buildEngine: function(){
      this.engine = 'e300';
      //todo sth
  },
  buildTyre: function(){
    this.tyre = 't200';
    //todo sth
  }
}

var car = new BaseCar();
setStyle(car);

setStyle(carObj){
  carObj.style = '達康書記玻璃貼紙';
}

 

外觀模式 門面模式

外觀模式(Facade),為子系統中的一組接口提供一個一致的界面,定義一個高層接口,這個接口使得這一子系統更加容易使用。  這是百度百科的定義描述。唉,其實這個模式很好理解,但是我一開始看到這個描述我就真的看不懂。“為接口提供一個界面”,這是什么描述,作為一個前端,界面就會理解成頁面或者肉眼能看到的界面。為接口提供一個界面是什么gui。。  好吧,再看下結構圖:

 

看了這個圖實際還不能徹底對它進行定性,當我再看到下面的代碼示例的時候,我就徹底清楚了外觀模式的定義:

function SubSystemOne(){
    //xxx
}  
function SubSystemTwo(){
    //xxx
}  
function SubSystemThree(){
    //xxx
}  
  
facade(){  
    SubSystemOne();  
    SubSystemTwo();  
    SubSystemThree();  
}

外觀模式是把一系列邏輯封裝到一個方法中,使當前邏輯更易使用,更易維護。那么我們實際前端開發中,會常常用到,說一個前端開發中的例子: 一個后台管理系統,有一個用戶列表頁面,添加用戶和修改用戶信息,觸發添加或者修改按鈕的時候,都是使用的同一個彈窗模板,然后根據不同的傳參去判斷是否是新增或者修改,再去改變彈框模板表單里的數據,添加的時候所有表單為空,編輯的時候把之前數據載入到表單。那么這個彈窗模板就是門面、也就是上圖的Facade。表單的html代碼是不變的,沒有必要寫兩個模板,所以這也可以叫代碼去重。

再舉一個例子: 上面說的彈框模板的表單,在新增用戶成功的回調函數里我們需要把表單里的數據給重置了,代碼如下:

 
addUser(){
  //獲取form數據, addModel 設置參數
  addModel(function(){
      //添加成功回調
      this.nodes.$form.address.val('');
      this.nodes.$form.cell.val('');
      this.nodes.$form.name.val('');
  })
}

這時編輯成功后也需要去執行上面回調里的重置代碼,我平時為了代碼去重,就需要寫一個單獨的重置方法,在兩個回調里調用,代碼如下:

addUser(){
  //獲取form數據, addModel 設置參數
  addModel(function(){
      //添加成功回調
      reset()
  })
}
editUser(){
  //獲取form數據, editModel 設置參數
  editModel(function(){
      //添加成功回調
      reset()
  })
}

function reset(){
  this.nodes.$form.address.val('');
  this.nodes.$form.cell.val('');
  this.nodes.$form.name.val('');
}

reset方法就是Facade,甚至說有更復雜的需求的時候,reset方法還可以傳參數,根據參數來重置某幾個表單,總之reset方法把重置表單的邏輯封裝在此方法內,其他地方需要調用重置相關功能,都用經過它才行,這樣可以減少代碼的耦合性,去除很多重復代碼,后期維護也非常清晰、改動也方便。  JQ的 $ 選擇器,其實也是此模式,它專門處理DOM選擇,集合了id、class等等選擇器,我們只需要在傳參數的時候前面加上#或者.,$('#id'),$('.class'),就能選擇相應的dom。

 

結語:

其實設計模式並不是很神秘,很牛逼沖天的技巧,也許你在不經意間寫出的代碼就是一種模式,這些設計模式只是針對一些寫法做了定義和命名。  這是此系列博客的第一篇。我不知道自己描述出來的設計模式,是否能被廣大同行所接受或者讓人能看明白。  如果你覺得此博客對你有幫助,歡迎留言或者點擊推薦,更多的反饋和支持可能是我堅持寫下去的理由和動力!

 

此系列博客目錄:

 

子慕談設計模式系列(一)

子慕談設計模式系列(二)——設計模式六大原則

子慕談設計模式系列(三)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM