天天看點

[設計模式] javascript 之 橋接模式

橋接模式說明

定義:分離抽象化與實作化,使之可以自由獨立的變化;

說明:由于軟體環境需求原因,使得類型抽象具有多種實作以自身變化定義等情況,這使得我們要分離抽象實作與具體實作,使得抽象化與實作化解耦,使之可以分開獨立的變化,使得兩者可以自由添加各自處理過程實作。

橋接模式就可以解決上面的問題,橋接模式的角色:

1. 抽象化角色,這個抽象化類中定義對 實作化接口定義的引用;

2. 修正抽象化角色,這個角色擴充并修改抽象化接口定義的實作,該修改用于引用實作化接口的實作的 (底層) 操作;

3. 實作化角色,定義實作化的接口定義;

4. 具體實作化,用于對于實作化接口定義的具體實作,它是一個底層實作;

從上面可以看出,橋接模式有兩個部分:抽象部分 以及 實作部分; 實作部分用于具體的底層實作,抽象部分用于封裝具體的底層現實;兩者間接口定義可以不一樣;

5. 繼承

什麼是繼承?
繼承就是一種類與類之間的關系,或是同種類型對象之間的一種關系,使用繼承後,繼承的類可以擁有被繼承的類所有的非私有成員或方法;

繼承是面向對象最重要的一種特性,是複用的一種重要方式.
繼承的優點,是可以實作代碼複用,減少代碼開發量,縮短開發時間.
繼承的缺點,破壞了封裝性,基類可能 “白箱” 暴露了方法的細節;

對象的封裝,是對軟體元件能成為一個良好子產品性的基礎,對象封裝一般包括屬性狀态跟方法行為組成,是對象世界的整體抽象,對象封裝裡,哪些是内部自己用的,哪些是供外部使用調用的,都可以完美的定義。

封裝性保證了對象減少與外部元件依賴,減少因為内部分改變而對其他對象造的影響。

對象的良好的封裝,可以保證對象間的“高内聚,低耦合”;

對于繼承複用來說,類編譯是靜态共享的,父類公共方法及成員可以直接的使用。

由于兩者的這種依賴關系,父類的修改,可能會影響到子類的實作,有時還得修改子類的某些操作等;反過來,子類可以重寫跟擴充父類的實作,父所定義的限制,容易被輕易的被子類所改變。

//
由于兩者在執行時即已存在固定的關系,是以當有新類型出現時,勢必重新定義子類,而無法在另一種層面上進行複用,就是當的類型出現時,當内部實作在現有子類與父類都無法滿足時,必須重新定義子類,甚至定義新的類(此可能展現的功能組成還是差不多,隻是新的實作不一樣了);      

6. 組合與聚合;

1>. 組合,這是一種強關聯,由部分組合成整體,是一種擁有的關系,具有相同的生命周期,部分脫離整将不具有意義;好比像狼群,獅子這種動态一樣,身體與四肢眼睛,就是整體與部分的組合關系;

2>. 聚合,這是一種弱關聯,兩者隻是一種包含關系,部分可以脫離整體獨立的存在;就是上面的狼群或獅子一樣,兩者都是群居動物,單個個體是獨立的存在,萬一脫離了群體,還是可以自己活動。

面向對象原則的第二點,就是盡量使用組合/聚合方式代替繼承複用。
組合/聚合,盡量少使用繼承,可以使對象盡量隻負責一項工作,在面對接口程式設計思想下,使用組合/聚合複用方法,因為聚合對象采用接口實作,接口可以抽象執行個體化或使用依賴注入的方法,指向對象的實作,是以當需要改變實作時,隻需要使接口指定需要的現實即可。(但是繼承這時基本是要重建子類繼承)      

7. 橋接模式的實作,靠得是抽象,多實作,以及抽象對實作角色的關聯,這種關聯,就是聚合關系;

8. 各種關系圖對比:

1>. 繼承關系:

[設計模式] javascript 之 橋接模式

當父類跟子類為 is-A 的關系時,用繼承;

2>. 組合關系

[設計模式] javascript 之 橋接模式

當子類為父類的一個不可分割部分時,用 組合關系, 兩者是 has-A 關系;

3>. 聚合關系是空心菱;

4>. 橋接關系圖;

[設計模式] javascript 之 橋接模式

上圖表示 抽象類與實作,是聚合的關系,實作隻是抽象的一種實作,由于抽象基于 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();      

其他說明

橋接模式,比較難懂,主要是要了解一些設計模式的原則,以及橋接模式的思想,原則就是不要爛用 繼承 因為繼承是編譯即定的他無法改變父的實作;面向對象設計建議使用組合/聚合複用方式來代替繼承複用,以及面向接口程式設計的好處;了解組合聚合複用給對象類封裝帶來的好處;

橋接模式要求對象抽象與實作抽象分開處理,這樣易于擴充,避免内部修改,展現了面向對象對擴充開放,對修改關閉的原則。這種模式雖然不常遇見但非常有用。

繼續閱讀