橋接模式說明
定義:分離抽象化與實作化,使之可以自由獨立的變化;
說明:由于軟體環境需求原因,使得類型抽象具有多種實作以自身變化定義等情況,這使得我們要分離抽象實作與具體實作,使得抽象化與實作化解耦,使之可以分開獨立的變化,使得兩者可以自由添加各自處理過程實作。
橋接模式就可以解決上面的問題,橋接模式的角色:
1. 抽象化角色,這個抽象化類中定義對 實作化接口定義的引用;
2. 修正抽象化角色,這個角色擴充并修改抽象化接口定義的實作,該修改用于引用實作化接口的實作的 (底層) 操作;
3. 實作化角色,定義實作化的接口定義;
4. 具體實作化,用于對于實作化接口定義的具體實作,它是一個底層實作;
從上面可以看出,橋接模式有兩個部分:抽象部分 以及 實作部分; 實作部分用于具體的底層實作,抽象部分用于封裝具體的底層現實;兩者間接口定義可以不一樣;
5. 繼承
什麼是繼承?
繼承就是一種類與類之間的關系,或是同種類型對象之間的一種關系,使用繼承後,繼承的類可以擁有被繼承的類所有的非私有成員或方法;
繼承是面向對象最重要的一種特性,是複用的一種重要方式.
繼承的優點,是可以實作代碼複用,減少代碼開發量,縮短開發時間.
繼承的缺點,破壞了封裝性,基類可能 “白箱” 暴露了方法的細節;
對象的封裝,是對軟體元件能成為一個良好子產品性的基礎,對象封裝一般包括屬性狀态跟方法行為組成,是對象世界的整體抽象,對象封裝裡,哪些是内部自己用的,哪些是供外部使用調用的,都可以完美的定義。
封裝性保證了對象減少與外部元件依賴,減少因為内部分改變而對其他對象造的影響。
對象的良好的封裝,可以保證對象間的“高内聚,低耦合”;
對于繼承複用來說,類編譯是靜态共享的,父類公共方法及成員可以直接的使用。
由于兩者的這種依賴關系,父類的修改,可能會影響到子類的實作,有時還得修改子類的某些操作等;反過來,子類可以重寫跟擴充父類的實作,父所定義的限制,容易被輕易的被子類所改變。
//
由于兩者在執行時即已存在固定的關系,是以當有新類型出現時,勢必重新定義子類,而無法在另一種層面上進行複用,就是當的類型出現時,當内部實作在現有子類與父類都無法滿足時,必須重新定義子類,甚至定義新的類(此可能展現的功能組成還是差不多,隻是新的實作不一樣了);
6. 組合與聚合;
1>. 組合,這是一種強關聯,由部分組合成整體,是一種擁有的關系,具有相同的生命周期,部分脫離整将不具有意義;好比像狼群,獅子這種動态一樣,身體與四肢眼睛,就是整體與部分的組合關系;
2>. 聚合,這是一種弱關聯,兩者隻是一種包含關系,部分可以脫離整體獨立的存在;就是上面的狼群或獅子一樣,兩者都是群居動物,單個個體是獨立的存在,萬一脫離了群體,還是可以自己活動。
面向對象原則的第二點,就是盡量使用組合/聚合方式代替繼承複用。
組合/聚合,盡量少使用繼承,可以使對象盡量隻負責一項工作,在面對接口程式設計思想下,使用組合/聚合複用方法,因為聚合對象采用接口實作,接口可以抽象執行個體化或使用依賴注入的方法,指向對象的實作,是以當需要改變實作時,隻需要使接口指定需要的現實即可。(但是繼承這時基本是要重建子類繼承)
7. 橋接模式的實作,靠得是抽象,多實作,以及抽象對實作角色的關聯,這種關聯,就是聚合關系;
8. 各種關系圖對比:
1>. 繼承關系:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcukjM4gzM1EDN0YDM3EzMw8CXxATNxAjMvw1M5YjMyEzLcd2bsJ2Lc12bj5ycn9Gbi52YuAzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
當父類跟子類為 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();
其他說明
橋接模式,比較難懂,主要是要了解一些設計模式的原則,以及橋接模式的思想,原則就是不要爛用 繼承 因為繼承是編譯即定的他無法改變父的實作;面向對象設計建議使用組合/聚合複用方式來代替繼承複用,以及面向接口程式設計的好處;了解組合聚合複用給對象類封裝帶來的好處;
橋接模式要求對象抽象與實作抽象分開處理,這樣易于擴充,避免内部修改,展現了面向對象對擴充開放,對修改關閉的原則。這種模式雖然不常遇見但非常有用。