javascript設計模式組合模式

antzone發表於2017-04-12

我們平時開發過程中,一定會遇到這種情況:同時處理簡單物件和由簡單物件組成的複雜物件,這些簡單物件和複雜物件會組合成樹形結構,在客戶端對其處理的時候要保持一致性。比如電商網站中的產品訂單,每一張產品訂單可能有多個子訂單組合,比如作業系統的資料夾,每個資料夾有多個子資料夾或檔案,我們作為使用者對其進行復制,刪除等操作時,不管是資料夾還是檔案,對我們操作者來說是一樣的。在這種場景下,就非常適合使用組合模式來實現。

基本知識:

組合模式:將物件組合成樹形結構以表示“部分-整體”的層次結構,組合模式使得使用者對單個物件和組合物件的使用具有一致性。

組合模式主要有三個角色:

(1)抽象元件(Component):抽象類,主要定義了參與組合的物件的公共介面

(2)子物件(Leaf):組成組合物件的最基本物件

(3)組合物件(Composite):由子物件組合起來的複雜物件

理解組合模式的關鍵是要理解組合模式對單個物件和組合物件使用的一致性,我們接下來說說組合模式的實現加深理解。

組合模式的實現:

(1).最簡單的組合模式:

HTML文件的DOM結構就是天生的樹形結構,最基本的元素醉成DOM樹,最終形成DOM文件,非常適用適用組合模式。

我們常用的jQuery類庫,其中組合模式的應用更是頻繁,例如經常有下列程式碼實現:

[JavaScript] 純文字檢視 複製程式碼
$(".test").addClass("noTest").remove("test");

這句簡單的程式碼就是獲取class包含test的元素,然後進行addClass和removeClass處理,其中不論$(“.test”)是一個元素,還是多個元素,最終都是通過統一的addClass和removeClass介面進行呼叫。

我們簡單模擬一下addClass的實現:

[JavaScript] 純文字檢視 複製程式碼
var addClass = function (eles, className) {
  if (eles instanceof NodeList) {
    for (var i = 0, length = eles.length; i < length; i++) {
      eles[i].nodeType === 1 && (eles[i].className += (' ' + className + ' '));
    }
  }
  else if (eles instanceof Node) {
    eles.nodeType === 1 && (eles.className += (' ' + className + ' '));
  }
  else {
    throw "eles is not a html node";
  }
}
addClass(document.getElementById("div3"), "test");
addClass(document.querySelectorAll(".div"), "test");

這段程式碼簡單的模擬了addClass的實現(暫不考慮相容性和通用性),很簡單地先判斷節點型別,然後根據不同型別新增className。對於NodeList或者是Node來說,客戶端呼叫都是同樣的使用了addClass這個介面,這個就是組合模式的最基本的思想,使部分和整體的使用具有一致性。

典型的例子:

前面我們提到一個典型的例子:產品訂單包含多個產品子訂單,多個產品子訂單組成一個複雜的產品訂單。由於Javascript語言的特性,我們將組合模式的三個角色簡化成2個角色:

(1)子物件:在這個例子中,子物件就是產品子訂單

(2)組合物件:這裡就是產品的總訂單

假設我們開發一個旅遊產品網站,其中包含機票和酒店兩種子產品,我們定義了子物件如下:

[JavaScript] 純文字檢視 複製程式碼
function FlightOrder() { }
FlightOrder.prototyp.create = function () {
  console.log("flight order created");
}
function HotelOrder() { }
HotelOrder.prototype.create = function () {
  console.log("hotel order created");
}

上面的程式碼定義了兩個類:機票訂單類和酒店訂單類,每個類都有各自的訂單建立方法。

接下來我們建立一個總訂單類:

[JavaScript] 純文字檢視 複製程式碼
function TotalOrders() {
  this.orderList = [];
}
TotalOrders.prototype.addOrder = function (order) {
  this.orderList.push(order);
}
TotalOrders.prototype.create = function (order) {
  for (var i = 0, length = this.orderList.length; i < length; i++) {
    this.orderList[i].create();
  }
}

這個物件主要有3個成員:訂單列表,新增訂單的方法,建立訂單的方法。

在客戶端使用的時候如下:

[JavaScript] 純文字檢視 複製程式碼
var flight = new FlightOrder();
flight.create();
 
var orders = new TotalOrders();
orders.addOrder(new FlightOrder());
orders.addOrder(new HotelOrder());
orders.create();

客戶端呼叫展示了兩種方式,一種是單一的建立機票訂單,一種是建立多張訂單,但最終都是通過create方法進行建立,這就是一個很典型的組合模式的應用場景。

總結:

組合模式並不難理解,它主要解決的是單一物件和組合物件在使用方式上的一致性問題。如果物件具有明顯的層次結構並且想要統一地使用它們,這就非常適合使用組合模式。在Web開發中,這種層次結構非常常見,很適合使用組合模式,尤其是對於JS來說,不用拘泥於傳統面嚮物件語言的形式,靈活地利用JS語言的特性,達到對部分和整體使用的一致性。

相關文章