適配器模式: 類適配器: 對象適配器: 1、定義:將一個介面轉換成客戶希望的另一個介面,適配器模式使介面不相容的那些類可以一起工作 2、模型結構: (1)目標抽象類(Target):客戶所期待得到的介面 (2)適配器類(Adapter):通過包裝一個需要適配的對象,把原介面轉換成目標介面 (3)適配 ...
適配器模式:
類適配器:
對象適配器:
1、定義:將一個介面轉換成客戶希望的另一個介面,適配器模式使介面不相容的那些類可以一起工作
2、模型結構:
(1)目標抽象類(Target):客戶所期待得到的介面
(2)適配器類(Adapter):通過包裝一個需要適配的對象,把原介面轉換成目標介面
(3)適配者類(Adaptee):需要適配的類
3、優點:
(1)將目標類和適配者類解耦,通過引入一個適配器類來重用現有的適配者類,而無須修改原有代碼
(2)增加了類的透明性和復用性,將具體的實現封裝在適配者類中,
對於客戶端類來說是透明的,而且提高了適配者的復用性
(3)靈活性和擴展性都非常好,通過使用配置文件,可以很方便地更換適配器,
也可以在不修改原有代碼的基礎上增加新的適配器類,完全符合“開閉原則”
(4)對象適配器:由於適配器類是適配者類的子類,
因此可以在適配器類中置換一些適配者的方法,使得適配器的靈活性更強
(5)類適配器:一個對象適配器可以把多個不同的適配者適配到同一個目標,也就是說,
同一個適配器可以把適配者類和它的子類都適配到目標介面
4、缺點:
(1)類適配器:對於Java、C#等不支持多重繼承的語言,一次最多只能適配一個適配者類,
而且目標抽象類只能為抽象類,不能為具體類,其使用有一定的局限性,
不能將一個適配者類和它的子類都適配到目標介面
(2)對象適配器:與類適配器模式相比,要想置換適配者類的方法就不容易。
如果一定要置換掉適配者類的一個或多個方法,就只好先做一個適配者類的子類,
將適配者類的方法置換掉,然後再把適配者類的子類當做真正的適配者進行適配,實現過程較為複雜
5、適用環境:
(1)系統需要使用現有的類,而這些類的介面不符合系統的需要
(2)想要建立一個可以重覆使用的類,用於彼此之間沒有太大關聯的一些類,包括一些可能在將來引進的類一起工作
// 例:220v ==> 110v // 類適配器模式 // Target interface Tareget { Output_110v(): void; } // 適配者類 Adaptee class PowerPort220v { Convert_110v(): void { console.log("Output 110V"); } } // 適配器類 Adapter, 繼承 Target 和 Adaptee class Adapter220v extends PowerPort220v implements Tareget { Output_110v(): void { this.Convert_110v(); } } let myAdapter: Tareget = new Adapter220v(); myAdapter.Output_110v(); // Output 110v // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // 對象適配器模式 // Target interface Target2 { Output_110v(): void; } // 適配者類 Adaptee class PowerPort2 { Convert_110v(): void { console.log("Output 110v"); } } // 適配器類 Adapter,依賴於 Adaptee,並實現介面 Target class Adapter2 implements Target2 { private adaptee: PowerPort2; constructor(adaptee: PowerPort2) { this.adaptee = adaptee; } Output_110v(): void { this.adaptee.Convert_110v(); } } let myPower: PowerPort2 = new PowerPort2(); let myAdapter2: Target2 = new Adapter2(myPower); myAdapter2.Output_110v(); // Output 110v
橋接模式:
1、定義:將抽象部分與它的實現部分分離,使它們都可以獨立地變化
2、模式結構:
(1)抽象類(Abstraction):定義抽象類,並包含一個對實現化對象的引用
(2)擴充抽象類(RefinedAbstraction):是抽象化角色的子類,實現父類中的業務方法,
並通過組合關係調用實現化角色中的業務方法
(3)實現類介面(Implementor):定義實現化角色的介面,供擴展抽象化角色調用
(4)具體實現類(ConcreteImplementor):給出實現化角色介面的具體實現
3、優點:
(1)分離抽象介面及其實現部分
(2)橋接模式有時類似於多繼承方案,但是多繼承方案違背了類的單一職責原則(即一個類只有一個變化的原則)
復用性比較差,而且多繼承結構中類的個數非常龐大,橋接模式是比多繼承方案更好的解決方法
(3)橋接模式提高了系統的可擴充性,在兩個變化維度中任意擴展一個維度,都不需要修改原有系統
(4)實現細節對客戶透明,可以對用戶隱藏實現細節
4、缺點:
(1)橋接模式的引入會增加系統的理解與設計難度,
由於聚合關聯關係建立在抽象層,要求開發者針對抽象進行設計與編程
(2)橋接模式要求正確識別出系統中兩個獨立變化的維度,因此其使用範圍具有一定的局限性,
如何正確識別兩個獨立維度也需要一定的經驗積累
5、適用環境:
(1)如果一個系統需要在構件的抽象化角色和具體化角色之間增加更多的靈活性,
避免在兩個層次之間建立靜態的繼承聯繫,通過橋接模式可以使它們在抽象層建立一個關聯關係
(2)抽象化角色和實現化角色可以通過繼承的方式獨立擴展而互不影響,
在程式運行時可以動態將一個抽象化子類的對象和一個實現化子類的對象進行組合
(3)一個類存在兩個獨立變化的維度,且這兩個維度都需要進行擴展
(4)雖然在系統中使用繼承是沒問題的,但由於抽象化角色和具體化角色需要獨立變化,設計要求需獨立管理他們
(5)對於那些不希望使用繼承或因為多層次繼承導致系統類的個數急劇增加的系統,橋接模式尤為適用
// Implementor interface Restaurant { taste(): string; getFlag(): string; } // ConcreteImplementor class GoodRes implements Restaurant { private flag: string = "好的"; private feedback: string = "Good"; taste(): string { return this.feedback; } getFlag(): string { return this.flag; } } class BadRes implements Restaurant { private flag: string = "差的"; private feedback: string = "Bad"; taste(): string { return this.feedback; } getFlag(): string { return this.flag; } } // Abstraction abstract class CityArea { protected res: Restaurant; constructor(res: Restaurant) { this.res = res; } abstract commentTaste(): void; } // RefinedAbstraction class GD extends CityArea { constructor(res: Restaurant) { super(res); } commentTaste(): void { console.log("廣東"+ this.res.getFlag() + "餐館感覺:" + this.res.taste()); } } let res1: Restaurant = new GoodRes(); let res2: Restaurant = new BadRes(); let area1: CityArea = new GD(res1); let area2: CityArea = new GD(res2); area1.commentTaste(); area2.commentTaste();
組合模式:
安全模式
透明模式
1、定義:也叫合成模式,有時又叫做部分-整體模式,主要是用來描述部分與整體的關係,
將對象組合成樹形結構以表示“部分-整體”的層次結構,使得用戶對單個對象和組合對象的使用具有一致性 2、組合模式的 2 種實現:安全模式和透明模式 (1)安全模式在抽象組件中只定義一些預設的行為或屬性,它是把樹枝節點和樹葉節點徹底分開; 透明模式是把用來組合使用的方法放到抽象類中,不管葉子對象還是樹枝對象都有相同的結構, 通過判斷確認是葉子節點還是樹枝節點,如果處理不當,這個會在運行期出現問題,不是很建議的方式 (2)安全模式與依賴倒置原則衝突;透明模式的好處就是它基本遵循了依賴倒轉原則,方便系統進行擴展 (3)安全模式在遍歷樹形結構的的時候需要進行強制類型轉換; 在透明模式下,遍歷整個樹形結構是比較容易的,不用進行強制類型轉換 註:“依賴倒置”原則指程式要依賴於抽象介面,不要依賴於具體實現3、模式結構:
(1)抽象構件角色(Component):定義參加組合對象的共有方法和屬性,可以定義一些預設的行為或屬性
(2)葉子構件(Leaf):葉子對象,其下再也沒有其他的分支,也就是遍歷的最小單位
(3)樹枝構件(Composite):樹枝對象,組合樹枝節點和葉子節點形成一個樹形結構,該模式的重點就在樹枝構件
4、優點:
(1)高層模塊調用簡單。局部和整體對調用者來說沒有任何區別,也就是說,
高層模塊不必關心自己處理的是單個對象還是整個組合結構,簡化了高層模塊的代碼
(2)節點自由增加。使用了組合模式後,我們可以看看,如果想增加一個樹枝節點、樹葉節點十分簡單,
只要找到它的父節點就成,非常容易擴展,符合開閉原則,對以後的維護非常有利5、缺點:樹枝樹葉直接使用了實現類,這在面向介面編程上是很不恰當的,
與依賴倒置原則衝突,它限制了你介面的影響範圍
6、適用環境:
(1)只要是樹形結構或者只要體現局部和整體的關係且這種關係還可能比較深,就考慮一下組合模式
(2)從一個整體中能夠獨立出部分模塊或功能的場景
(3)維護和展示部分-整體關係的場景
// 安全模式 // 抽象構件 Component abstract class PersonMode { private name: string; private sex: string; private age: number; constructor(name: string, sex: string, age: number) { this.name = name; this.sex = sex; this.age = age; } getPersonInfo(): string { let info: string = "name: "+ this.name + "\tsex: " + this.sex + "\tage: " + this.age; return info; } } // 樹葉構件 Leaf class PersonLeaf extends PersonMode { constructor(name: string, sex: string, age: number) { super(name, sex, age); } } // 樹枝構件 Composite class PersonBranch extends PersonMode { private personModeList: PersonMode[] = new Array(); constructor(name: string, sex: string, age: number) { super(name, sex, age); } addPerson(person: PersonMode): void { this.personModeList.push(person); } getPersonList(): PersonMode[] { return this.personModeList; } } // 第一代 J let OneJ: PersonBranch = new PersonBranch("第一代 J", "男", 150); // 第一代 J 的 3 個兒子 let JA: PersonBranch = new PersonBranch("JA", "男", 70); let JB: PersonBranch = new PersonBranch("JB", "男", 60); let JC: PersonBranch = new PersonBranch("JC", "男", 50); // JA 的 2 個兒子 let JA1: PersonBranch = new PersonBranch("JA1", "男", 40); let JA2: PersonBranch = new PersonBranch("JA2", "男", 30); // JA1 的 2 個兒子 let JA1_1: PersonBranch = new PersonBranch("JA1_1", "男", 18); let JA1_2: PersonBranch = new PersonBranch("JA1_2", "男", 16); // 組裝族譜 function getPersonInfo(): PersonBranch { OneJ.addPerson(JA); OneJ.addPerson(JB); OneJ.addPerson(JC); JA.addPerson(JA1); JA.addPerson(JA2); JA1.addPerson(JA1_1); JA1.addPerson(JA1_2); return OneJ; } function showTree(root: PersonBranch): void { console.log(root.getPersonInfo()); let list = root.getPersonList() for (let i = 0; i < list.length; ++i) { // 這裡使用 for...of 出現錯誤 if (list[i] instanceof PersonLeaf) { console.log(list[i].getPersonInfo()); } else { showTree(<PersonBranch>list[i]); } } } let personBranch: PersonBranch = getPersonInfo(); showTree(personBranch);