[設計模式] javascript 之 橋接模式
橋接模式說明 定義:分離抽象化與實現化,使之可以自由獨立的變化; 說明:由於軟體環境需求原因,使得類型抽象具有多種實現以自身變化定義等情況,這使得我們要分離抽象實現與具體實現,使得抽象化與實現化解耦,使之可以分開獨立的變化,使得兩者可以自由添加各自處理過程實現。 橋接模式就可以解決上面的問題,橋接模式的角色: 1. 抽象化角色,這個抽象化類中定義對 實現化介面定義的引用; 2. 修正抽象化角色,這個角色擴充並修改抽象化介面定義的實現,該修改用於引用實現化介面的實現的 (底層) 操作; 3. 實現化角色,定義實現化的介面定義; 4. 具體實現化,用於對於實現化介面定義的具體實現,它是一個底層實現; 從上面可以看出,橋接模式有兩個部分:抽象部分 以及 實現部分; 實現部分用於具體的底層實現,抽象部分用於封裝具體的底層現實;兩者間介面定義可以不一樣; 5. 繼承 什麼是繼承?繼承就是一種類與類之間的關係,或是同種類型對象之間的一種關係,使用繼承後,繼承的類可以擁有被繼承的類所有的非私人成員或方法; 繼承是物件導向最重要的一種特性,是複用的一種重要方式.繼承的優點,是可以實現代碼複用,減少代碼開發量,縮短開發時間.繼承的缺點,破壞了封裝性,基類可能 “白箱” 暴露了方法的細節; 對象的封裝,是對軟體組件能成為一個良好模組性的基礎,對象封裝一般包括屬性狀態跟方法行為組成,是對象世界的整體抽象,對象封裝裡,哪些是內部自己用的,哪些是供外部使用調用的,都可以完美的定義。 封裝性保證了對象減少與外部組件依賴,減少因為內部分改變而對其他對象造的影響。 對象的良好的封裝,可以保證對象間的“高內聚,低耦合”; 對於繼承複用來說,類編譯是靜態共用的,父類公用方法及成員可以直接的使用。 由於兩者的這種依賴關係,父類的修改,可能會影響到子類的實現,有時還得修改子類的某些操作等;反過來,子類可以重寫跟擴充父類的實現,父所定義的約束,容易被輕易的被子類所改變。 //由於兩者在執行時即已存在固定的關係,所以當有新類型出現時,勢必重新定義子類,而無法在另一種層面上進行複用,就是當的類型出現時,當內部實現在現有子類與父類都無法滿足時,必須重新定義子類,甚至定義新的類(此可能展現的功能組成還是差不多,只是新的實現不一樣了); 6. 組合與彙總; 1>. 組合,這是一種強關聯,由部分組合成整體,是一種擁有的關係,具有相同的生命週期,部分脫離整將不具有意義;好比像狼群,獅子這種動態一樣,身體與四肢眼睛,就是整體與部分的組合關係; 2>. 彙總,這是一種弱關聯,兩者只是一種內含項目關聯性,部分可以脫離整體獨立的存在;就是上面的狼群或獅子一樣,兩者都是群居動物,單個個體是獨立的存在,萬一脫離了群體,還是可以自己活動。 物件導向原則的第二點,就是盡量使用組合/彙總方式代替繼承複用。組合/彙總,盡量少使用繼承,可以使對象盡量只負責一項工作,在面對介面編程思想下,使用組合/彙總複用方法,因為彙總對象採用介面實現,介面可以抽象執行個體化或使用依賴注入的方法,指向對象的實現,因此當需要改變實現時,只需要使介面指定需要的現實即可。(但是繼承這時基本是要重建子類繼承) 7. 橋接模式的實現,靠得是抽象,多實現,以及抽象對實現角色的關聯,這種關聯,就是彙總關係; 8. 各種關係圖對比: 1>. 繼承關係: 當父類跟子類為 is-A 的關係時,用繼承; 2>. 組合關係 當子類為父類的一個不可分割部分時,用 組合關係, 兩者是 has-A 關係; 3>. 彙總關係是空心菱; 4>. 橋接關係圖; 表示 抽象類別與實現,是彙總的關係,實現只是抽象的一種實現,由於抽象基於 interface 介面編程,所以當 有另外的實現時,只需讓 interface 指向需要的實現即可; 橋接模式分兩部分,一部分是事物抽象,比如一個發訊息的功能,可能有郵件跟簡訊發送幾種;一部分可以說是事物行為實現抽象,比如訊息內容的性質,比如說普通的,緊急的等; 我們也可以把實現部分看成是一種通用的組件抽象; 橋接模式的思想,就是讓我們獨立各種抽象的實現,像上面的 郵件跟簡訊,兩者基於同一個介面interface實現,當用戶端有需求時,可以在兩者之間通過介面方便的切換; 情境執行個體: 比如我們的教學,教學有時是在普通教室進行的,有的是在多媒體教室進行的,有的是在實驗室進行;教學的內容有英語,語文,物理,數學等; 當然說,普通教室是什麼都可以教的,多媒體教室也是什麼課程都可以用的,而且使用起來,效果更好,學生更喜歡; 在這裡,在什麼地方教書,是種方式,而課程是一種內容;方式下又可以對各內容的實現; 如果只是採用繼承的方式,那麼每個內容的實現都得自己實現,這樣會產生很多重複的代碼; 這時我們可以採用橋接的模式,對方式跟內容進行各自己的實現;方式再與實現使用 彙總方式 關聯起來; 源碼執行個體 1. 定義各種課程. 教程內容抽象實現; function Couse() { } Couse.prototype = { chinese: function() { console.log('教語文果'); }, english: function() { console.log('教英文課'); }, physics: function() { console.log('教物理課'); }} 接下來對教學方式,進行抽象: 1>. 普通教室: function ordinary(couse) { this.couse = couse; this.where = '普通教室';} ordinary.prototype = { chinese: function() { console.log('在 ' + this.where + ' '); this.couse.chinese(); }, english: function() { console.log('在 ' + this.where + ' '); this.couse.english(); }, physics: function() { console.log('在 ' + this.where + ' '); this.couse.physics(); }} 2>. 多媒體教室: function multimedia(couse) { this.couse = couse; this.where = '普通教室';} multimedia.prototype = { chinese: function() { console.log('在 ' + this.where + ' '); this.couse.chinese(); //定義新內容; console.log('接下來將播相關的課程影片') }, english: function() { console.log('在 ' + this.where + ' '); this.couse.english(); //定義新內容; console.log('請戴好耳機認真聽;') }, physics: function() { console.log('在 ' + this.where + ' '); this.couse.physics(); }} 使用方法: //普通教室var couse = new Couse();var classroom = ordinary(couse);classroom.chinese(); //多媒體教室classroom = new multimeida(couse);classroom.chinese(); 其他說明 橋接模式,比較難懂,主要是要理解一些設計模式的原則,以及橋接模式的思想,原則就是不要爛用 繼承 因為繼承是編譯即定的他無法改變父的實現;物件導向設計建議使用組合/彙總複用方式來代替繼承複用,以及面向介面編程的好處;理解組合彙總複用給對象類封裝帶來的好處; 橋接模式要求對象抽象與實現抽象分開處理,這樣易於擴充,避免內部修改,體現了物件導向對擴充開放,對修改關閉的原則。這種模式雖然不常遇見但非常有用。