1. 程式人生 > >設計模式 #4 (裝飾器模式、介面卡模式)

設計模式 #4 (裝飾器模式、介面卡模式)

# 設計模式 #4 (裝飾器模式、介面卡模式) --- **文章中所有工程程式碼和`UML`建模檔案都在我的這個`GitHub`的公開庫--->[DesignPattern](https://github.com/L1ng14/DesignPattern)。**`Star`來一個好嗎?秋梨膏! --- ## 裝飾器模式 簡述:**在不改變現有物件結構的情況下,為現有物件新增新功能。** 需求:玩過那種女孩換裝那種遊戲嗎?什麼?沒玩過?猛男必玩的呀!現在需要選擇套裝進行裝扮,並計算當前服裝的花費。 #### 反例 #1: ~~~java public abstract class Suit { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Suit(String name){ this.name =name; } abstract int price(); @Override public String toString() { return "Suit{" + "name='" + this.getName() + '\'' + //注意這裡使用this.getName() "price='" + price() + '\'' + '}'; } } public class Suit_01 extends Suit{ public Suit_01() { super("猛男套裝"); } @Override public int price() { return 1899; } } public class Suit_02 extends Suit{ public Suit_02() { super("青春套裝"); } @Override public int price() { return 2199; } } /*=====================客戶端=========================*/ public class negtive { public static void main(String[] args) { Person girl = new Person("Mary",new suit_02()); System.out.println(girl); System.out.println(" "); Person boy = new Person("Jack",new suit_01()); System.out.println(boy); } } ~~~ 這樣一看,面向抽象程式設計,分離的這麼好,這是一次不錯的設計。 `UML`類圖如下: ![image-2020095242525418145112051](https://i.loli.net/2020/09/18/ZFSiNhM5BDk2XUy.png) 需求:現在遊戲出了新服飾,需要為原有的一個人物新增新的服飾進行裝扮,要求是不改變原有的裝扮。 學習[七大設計原則](https://www.cnblogs.com/l1ng14/p/13662445.html)都知道,[開閉原則](https://www.cnblogs.com/l1ng14/p/13662445.html#開閉原則)是一個大多數設計模式都遵守的設計原則,此需求要求不改變原有裝扮其實就是要求不改變原有物件--`Suit`的結構。 - 如果單純地繼承每一個原有套裝類`Suit`,重寫相關方法進行類的增加,可能會造成程式碼的臃腫。 - 長期以往隨著新增服飾增加,也將導致套裝類`Suit`的爆炸式增長。 此時熟悉換裝遊戲的朋友,不,熟悉設計模式的朋友們就知道要使用裝飾器模式進行設計了。 #### 正例 #1: ~~~java //抽象服飾類 public abstract class Decorate extends Suit{ protected Suit suit; public Decorate(Suit suit){ super(suit.getName()); this.suit = suit; } } //服飾——01 public class Canvas_Shoes extends Decorate{ public Canvas_Shoes(Suit suit) { super(suit); } @Override public String getName() { return super.getName()+" +帆布鞋"; } @Override public int price() { return suit.price()+399; } } //服飾——02 public class Skirt extends Decorate { public Skirt(Suit suit) { super(suit); } @Override public String getName() { return suit.getName()+" +短裙"; } @Override public int price() { return suit.price()+499; } } ~~~ ~~~java /*=================客戶端====================*/ public class postive { public static void main(String[] args) { Suit_02 girl_suit = new Suit_02(); Skirt skirt = new Skirt(girl_suit); Canvas_Shoes canvas_shoes = new Canvas_Shoes(skirt); System.out.println(canvas_shoes); } } ~~~ `UML`類圖如下: ![image-20200918204104962](https://i.loli.net/2020/09/18/thCIgJBmriEPKO6.png) 這樣一來,增加套裝類,服飾類都不會違反[開閉原則](https://www.cnblogs.com/l1ng14/p/13662445.html#開閉原則)了。雖然還是會產生很多類。 但是客戶端需要使用的時候只需要**用新增的裝飾器類(繼承重寫`Decorate`)巢狀使用進行裝飾套裝類(繼承重寫`Suit`)即可**。簡單點說就是增加新一層外包裝--套娃,哈哈。 `Java`中,`java.io`就是用了裝飾器模式進行`API`設計: ![image-20200913655892](https://i.loli.net/2020/09/18/MRgCXFLujnpfwvV.png) ![image-20200918183831427](https://i.loli.net/2020/09/18/lCu6Ij1EsPBU2zx.png) 甚至我們還可以自己寫一個加到`java.io`中: ~~~java public class MyBufferReader extends Reader { private Reader in; public MyBufferReader(Reader in){ this.in = in; } public String readLine() throws IOException { StringBuilder sb = new StringBuilder(); int read; while(true){ read = in.read(); if (read == '\r') continue; if (read == '\n' || read == -1){ break; } sb.append((char)read); } if (sb.toString().length() == 0){ if (read == '\n'){ return ""; }else { return null; } }else { return sb.toString(); } } @Override public int read(char[] cbuf, int off, int len) throws IOException { return 0; } @Override public void close() throws IOException { in.close(); } /*===================客戶端===========================*/ public static void main(String[] args) throws IOException { Reader in = new FileReader("E:\\1.txt"); MyBufferReader myBufferReader = new MyBufferReader(in); // BufferedReader br = new BufferedReader(myBufferReader); 可以層層進行套娃包裝 String line; while(( line = myBufferReader.readLine() ) != null){ System.out.println(line); } } } ~~~ ![image-20200918192559454](https://i.loli.net/2020/09/18/VMRt39AyDPkmSpe.png) 輸出我近期投訴學校店大欺客的移動寬頻寫的小作文。 ![image-20200918192730552](https://i.loli.net/2020/09/19/Q4hnHUoCGzpd3Or.png) 在檢視抽象類`Reader`時。可以看到我們自己寫的`MyBufferReader`已經融入到裝飾類的群中,可以進行包裝其他`Reader`的子類,也可以被其他`Reader`子類包裝使用。 ## 介面卡模式 簡述:介面卡模式(Adapter),將一個類的介面轉換成客戶希望的另外一個介面。Adapter模式使得原本由於介面不相容而不能一起工作的那些類可以一起工作。 這次不叫反例,叫前提條件。 現在中國球員去`NBA`發展,可是初來乍到,會有語言不同的情況,現有設計類如下: ~~~java public interface Foreign_Player { void Speak_Foreign_Language(); String getName(); } public class CHN_player implements Foreign_Player { private String name; public CHN_player(String name) { this.name =name; } public String getName() { return name; } @Override public void Speak_Foreign_Language() { System.out.println("用中文說戰術。。。。"); } } ~~~ ~~~java public interface Native_Player { void speak_English(); String getName(); } public class NBA_player implements Native_Player { private String name; public NBA_player(String name) { this.name =name; } public String getName() { return name; } @Override public void speak_English() { System.out.println(name+"Talk about Tactics in English。。。。"); } } ~~~ 現在需要讓所有球員用英文傳達戰術,中國球員現學肯定來不及了(不能現改中國球員`Foreign_Player`介面違反[開閉原則](https://www.cnblogs.com/l1ng14/p/13662445.html#開閉原則)),那就需要一個翻譯人員,介面卡模式就派上用場了。 ~~~java public class Adapter_Translator implements Native_Player { private Foreign_Player foreign_player; public Adapter_Translator(Foreign_Player foreign_player) { this.foreign_player = foreign_player; } public String getName() { return foreign_player.getName()+" 的翻譯人員"; } @Override public void speak_English() { System.out.println("翻譯人員翻譯中文戰術成英語給隊友-------"); } } ~~~ ~~~java /*=================客戶端====================*/ public class postive { public static void main(String[] args) { CHN_player yaoming = new CHN_player("yaoming"); System.out.println(yaoming.getName()); yaoming.Speak_Foreign_Language(); //進行適配 Native_Player translator = new Adapter_Translator(yaoming); System.out.println(translator.getName()); translator.speak_English(); } } ~~~ `UML`類圖如下: ![image-202009191sss62235302](https://i.loli.net/2020/09/19/yLQJMVI4W1quYaG.png) 因為有了翻譯人員,中國球員不會因為語言不通的原因導致球員之間無法溝通的問題,這時候,中國球員就好像一個本土球員一樣在賽場上打球。 也就是兩個實現不同介面的、不存在聯絡的類,在不修改原始碼的前提下,通過介面卡,可以實現互通互用,遵守[開閉原則](https://www.cnblogs.com/l1ng14/p/13662445.html#開閉原則)。 簡單總結一下介面卡的編寫套路: - **繼承需要適配成的抽象類,或者實現需要適配的介面。**(例子中需要適配成`Native_Player`,介面卡`Adapter_Translator`就實現`Native_Player`) - **組合原來不適配的抽象類或者介面。**(例子中,介面卡`Adapter_Translator`組合了`Foreign_Player`介面) - > 這裡是體現了七大設計原則中的[組合優於繼承](https://www.cnblogs.com/l1ng14/p/13662445.html#組合優於繼承)。 得此套路,大功即