標籤:div pos 通訊 特性 protect 遍曆 方法 抽象類別 地方
物件導向的JavaScript --- 封裝封裝
封裝的目的是將資訊隱藏。一般而言,我們討論的封裝是封裝資料和封裝實現。真正的封裝為更廣義的封裝,不僅包括封裝資料和封裝實現,還包括封裝類型和封裝變化。
封裝資料
在許多語言的對象系統中,封裝資料是由文法解析來實現的,這些語言也許提供了 private、public、protected 等關鍵字來提供不同的存取權限。但JavaScript並沒有提供對這些關鍵字的支援,我們只能依賴變數的範圍來實現封裝特性,而且只能類比出 public 和 private 這兩種封裝性。
除了 ECMAScript 6 中提供的 let 之外,一般我們通過函數來建立範圍:
var myObject = (fucntion(){ var __name = ‘sven‘; // 私人(private變數) return { getname: funvtion (){ // 公開(public)方法 return __name; } }})();console.log(myObject.getName()); // 輸出: svenconsole.log(myObject.__name); // 輸出 undefined
外值得一提的是,在 ECAMScript 6 中,還可以通過 Symbol 建立私人屬性。
封裝實現
封裝的目的是將資訊隱藏,封裝應該被視為“任何形式的封裝”,也就是說,封裝不僅僅是隱藏資料,還包括隱藏實現細節、設計細節以及隱藏對象的類型等。
從封裝實現細節來講,封裝使得對象內部的變化對其他對象而言是透明的,也就是不可見的。對象對它自己的行為負責。其他對象或者使用者都不關心它的內部實現。封裝使得對象之間的耦合變鬆散,對象之間只通過暴露的API介面來通訊。當我們修改一個對象時,可以隨意地修改它的內部實現,只要對外的介面沒有變化,就不會影響到程式的其他功能。
封裝實現細節的例子非常之多。拿迭代器來說明,迭代器的作用是在不暴露一個彙總對象的內部表示的前提下,提供一種方式來順序訪問這個彙總對象。我們編寫了一個each函數,它的作用就是遍曆一個彙總對象,使用這個 each 函數的人不用關心它的內部是怎樣實現的,只要它提供的功能正確便可以。即使each函數修改了內部原始碼,只要對外的介面或者調用方式沒有變化,使用者就不用關心它內部實現的改變。
封裝類型
封裝類型是靜態類型語言中一種重要的封裝方式。一般而言,封裝類型是通過抽象類別和介面來進行的。把對象的真正類型隱藏在抽象類別或者介面之後,相比對象的類型,客戶更關心對象的行為。在許多靜態語言的設計模式中,想方設法地去隱藏對象的類型,也是促使這些模式誕生的原因之一。比如Factory 方法模式、組合模式等。當然在 JavaScript 中,並沒有對抽象類別和介面的支援。 JavaScript本身也是一門類型模糊的語言。在封裝類型方面, JavaScript 沒有能力,也沒有必要做得更多。對於JavaScript的設計模式實現來說,不區分類型是一種失色,也可以說是一種解脫。
封裝變化
從設計模式的角度出發,封裝在更重要的層面體現為封裝變化。
《設計模式》一書曾提到如下文字:
? “考慮你的設計中哪些地方可能變化,這種方式與關注會導致重新設計的原因相反。它不是考慮什麼時候會迫使你的設計改變,而是考慮你怎樣才能夠在不重新設計的情況下進行改變。這裡的關鍵在於封裝發生變化的概念,這是許多設計模式的主題。”
這段文字即是《設計模式》提到的“找到變化並封裝之”。《設計模式》一書中共歸納總結了23種設計模式。從意圖上區分, 這 23 種設計模式分別被劃分為:
建立型模式: 建立一個對象是種抽象行為,具體建立什麼對象是可以變化的,建立型模式的目的是封裝建立對象的變化。
結構型模式: 封裝的是對象之間的組合關係。
行為型模式: 封裝的是對象的行為變化。
通過封裝變化的方式,把系統中穩定不變的部分和容易變化的部分隔離開來,在系統的演變過程中,我們只需要替換那些容易變化的部分,如果這些部分是已經封裝好的,替換起來也相對容易。這可以最大程度地保證程式的穩定性和可擴充性。
從《設計模式》副標題“可複用物件導向軟體的基礎”可以知道,這本書理應教我們如何編寫可複用的物件導向程式。這本書把大多數筆墨都放在如何封裝變化上面,這跟編寫可複用的物件導向程式是不矛盾的。當我們想辦法把程式中變化的部分封裝好之後,剩下的即是穩定而可複用的部分了。
物件導向的JavaScript --- 封裝