作者:zuoxiaolong8810(左瀟龍),轉載請注明出處。
???????????????? LZ到目前已經寫了九個設計模式,回過去看看,貌似寫的有點凌亂,LZ后面會盡量改進。
???????????????? 那么本章LZ和各位讀友討論一個與JAVA中IO有著不解情緣的設計模式,裝飾器模式。
?????????????????定義:裝飾模式是在不必改變原類文件和使用繼承的情況下,動態的擴展一個對象的功能。它是通過創建一個包裝對象,也就是裝飾來包裹真實的對象。
?????????????????這一個解釋,引自百度百科,我們注意其中的幾點。
?????????????????1,不改變原類文件。
?????????????????2,不使用繼承。
???????????????? 3,動態擴展。
???????????????? 上述三句話一語道出了裝飾器模式的特點,下面LZ給出裝飾器模式的類圖,先上圖再解釋。
??????????????????從圖中可以看到,我們裝飾的是一個接口的任何實現類,而這些實現類也包括了裝飾器本身,裝飾器本身也可以再被裝飾。
??????????????????另外,這個類圖只是裝飾器模式的完整結構,但其實里面有很多可以變化的地方,LZ給出如下兩條。
??????????????????1,Component接口可以是接口也可以是抽象類,甚至是一個普通的父類(這個強烈不推薦,普通的類作為繼承體系的超級父類不易于維護)。
????????????????? 2,裝飾器的抽象父類Decorator并不是必須的。
???????????????? 那么我們將上述標準的裝飾器模式,用我們熟悉的JAVA代碼給詮釋一下。首先是待裝飾的接口Component。
[java]?view plaincopy
package?com.decorator;????public?interface?Component?{????????void?method();????????}??
???????????????? 接下來便是我們的一個具體的接口實現類,也就是俗稱的原始對象,或者說待裝飾對象。
[java]?view plaincopy
package?com.decorator;????public?class?ConcreteComponent?implements?Component{????????public?void?method()?{??????????System.out.println("原來的方法");??????}????}??
???????????????? 下面便是我們的抽象裝飾器父類,它主要是為裝飾器定義了我們需要裝飾的目標是什么,并對Component進行了基礎的裝飾。
[java]?view plaincopy
package?com.decorator;????public?abstract?class?Decorator?implements?Component{????????protected?Component?component;????????public?Decorator(Component?component)?{??????????super();??????????this.component?=?component;??????}????????public?void?method()?{??????????component.method();??????}????????}??
????????????????? 再來便是我們具體的裝飾器A和裝飾器B。
[java]?view plaincopy
package?com.decorator;????public?class?ConcreteDecoratorA?extends?Decorator{????????public?ConcreteDecoratorA(Component?component)?{??????????super(component);??????}????????????public?void?methodA(){??????????System.out.println("被裝飾器A擴展的功能");??????}????????public?void?method(){??????????System.out.println("針對該方法加一層A包裝");??????????super.method();??????????System.out.println("A包裝結束");??????}??}??
[java]?view plaincopy
package?com.decorator;????public?class?ConcreteDecoratorB?extends?Decorator{????????public?ConcreteDecoratorB(Component?component)?{??????????super(component);??????}????????????public?void?methodB(){??????????System.out.println("被裝飾器B擴展的功能");??????}????????public?void?method(){??????????System.out.println("針對該方法加一層B包裝");??????????super.method();??????????System.out.println("B包裝結束");??????}??}??
??????????????? 下面給出我們的測試類。我們針對多種情況進行包裝。
[java]?view plaincopy
package?com.decorator;????public?class?Main?{????????public?static?void?main(String[]?args)?{??????????Component?component?=new?ConcreteComponent();??????????System.out.println("------------------------------");??????????component.method();??????????ConcreteDecoratorA?concreteDecoratorA?=?new?ConcreteDecoratorA(component);??????????System.out.println("------------------------------");??????????concreteDecoratorA.method();??????????concreteDecoratorA.methodA();??????????ConcreteDecoratorB?concreteDecoratorB?=?new?ConcreteDecoratorB(component);??????????System.out.println("------------------------------");??????????concreteDecoratorB.method();??????????concreteDecoratorB.methodB();??????????concreteDecoratorB?=?new?ConcreteDecoratorB(concreteDecoratorA);??????????System.out.println("------------------------------");??????????concreteDecoratorB.method();??????????concreteDecoratorB.methodB();??????}??}??
???????????????? 下面看下我們運行的結果,到底是產生了什么效果。
???????????????從此可以看到,我們首先是使用的原始的類的方法,然后分別讓A和B裝飾完以后再調用,最后我們將兩個裝飾器一起使用,再調用該接口定義的方法。
???????????????上述當中,我們分別對待裝飾類進行了原方法的裝飾和新功能的增加,methodA和methodB就是新增加的功能,這些都是裝飾器可以做的,當然兩者并不一定兼有,但一般至少會有一種,否則也就失去了裝飾的意義。
?????????????? 另外,文章開篇就說道了IO與裝飾器的情緣,相信各位就算不太清楚,也都大致聽說過JAVA的IO是裝飾器模式實現的,所以LZ也不再廢話,在給出一個標準的模板示例以后,直接拿出IO的示例,我們真槍實彈的來。
?????????????? 下面LZ直接給出IO包中的部分裝飾過程,上面LZ加了詳細的注釋以及各個裝飾器的功能演示,各位可以和上面標準的裝飾器模式對比一下,LZ不得不感嘆,IO與裝飾器的孽緣。
[java]?view plaincopy
package?com.decorator;????import?java.io.BufferedInputStream;??import?java.io.BufferedReader;??import?java.io.DataInputStream;??import?java.io.FileInputStream;??import?java.io.IOException;??import?java.io.InputStream;??import?java.io.InputStreamReader;??import?java.io.LineNumberReader;??import?java.io.PushbackInputStream;??import?java.io.PushbackReader;????public?class?IOTest?{????????????????public?static?void?main(String[]?args)?throws?IOException,?ClassNotFoundException?{????????????????????final?String?filePath?=?"E:/myeclipse?project/POITest/src/com/decorator/test.txt";??????????????????????????????????????????????????InputStream?inputStream?=?new?FileInputStream(filePath);??????????final?int?len?=?inputStream.available();??????????System.out.println("FileInputStream不支持mark和reset:"?+?inputStream.markSupported());????????????????????System.out.println("---------------------------------------------------------------------------------");??????????????????????????????????????????????????BufferedInputStream?bufferedInputStream?=?new?BufferedInputStream(inputStream);??????????System.out.println("BufferedInputStream支持mark和reset:"?+?bufferedInputStream.markSupported());??????????bufferedInputStream.mark(0);??????????char?c?=?(char)?bufferedInputStream.read();??????????System.out.println("LZ文件的第一個字符:"?+?c);??????????bufferedInputStream.reset();??????????c?=?(char)?bufferedInputStream.read();??????????System.out.println("重置以后再讀一個字符,依然會是第一個字符:"?+?c);??????????bufferedInputStream.reset();????????????????????System.out.println("---------------------------------------------------------------------------------");??????????????????????????????????????????????????DataInputStream?dataInputStream?=?new?DataInputStream(bufferedInputStream);??????????dataInputStream.reset();??????????System.out.println("DataInputStream現在具有readInt,readChar,readUTF等功能");??????????int?value?=?dataInputStream.readInt();????????????????????String?binary?=?Integer.toBinaryString(value);??????????int?first?=?binary.length()?%?8;??????????System.out.print("使用readInt讀取的前四個字符:");??????????for?(int?i?=?0;?i?<?4;?i++)?{??????????????if?(i?==?0)?{??????????????????System.out.print(((char)Integer.valueOf(binary.substring(0,?first),?2).intValue()));??????????????}else?{??????????????????System.out.print(((char)Integer.valueOf(binary.substring((?i?-?1?)?*?8?+?first,?i?*?8?+?first),?2).intValue()));??????????????}??????????}??????????System.out.println();????????????????????System.out.println("---------------------------------------------------------------------------------");????????????????????????????????????????inputStream?=?new?FileInputStream(filePath);??????????PushbackInputStream?pushbackInputStream?=?new?PushbackInputStream(inputStream,len);??????????System.out.println("PushbackInputStream裝飾以后支持退回操作unread");??????????byte[]?bytes?=?new?byte[len];??????????pushbackInputStream.read(bytes);??????????System.out.println("unread回退前的內容:"?+?new?String(bytes));??????????pushbackInputStream.unread(bytes);??????????bytes?=?new?byte[len];??????????pushbackInputStream.read(bytes);??????????System.out.println("unread回退后的內容:"?+?new?String(bytes));????????????????????System.out.println("---------------------------------------------------------------------------------");??????????????????????????????????????????????????inputStream?=?new?FileInputStream(filePath);??????????InputStreamReader?inputStreamReader?=?new?InputStreamReader(inputStream,"utf-8");??????????System.out.println("InputStreamReader有reader的功能,比如轉碼:"?+?inputStreamReader.getEncoding());????????????????????System.out.println("---------------------------------------------------------------------------------");????????????????????BufferedReader?bufferedReader?=?new?BufferedReader(inputStreamReader);??????????System.out.println("BufferedReader有readLine等功能:"?+?bufferedReader.readLine());????????????????????System.out.println("---------------------------------------------------------------------------------");????????????????????LineNumberReader?lineNumberReader?=?new?LineNumberReader(inputStreamReader);??????????System.out.println("LineNumberReader有設置行號,獲取行號等功能(行號從0開始),當前行號:"?+?lineNumberReader.getLineNumber());????????????????????System.out.println("---------------------------------------------------------------------------------");??????????????????????????????inputStreamReader?=?new?InputStreamReader(new?FileInputStream(filePath));??????????PushbackReader?pushbackReader?=?new?PushbackReader(inputStreamReader,len);??????????System.out.println("PushbackReader是擁有退回操作的reader對象");??????????char[]?chars?=?new?char[len];??????????pushbackReader.read(chars);??????????System.out.println("unread回退前的內容:"?+?new?String(chars));??????????pushbackReader.unread(chars);??????????chars?=?new?char[len];??????????pushbackReader.read(chars);??????????System.out.println("unread回退后的內容:"?+?new?String(chars));??????}??}??
???????????????????? 上述便是IO的裝飾器使用,其中InputStream就相當于上述的Component接口,只不過這里是一個抽象類,這是我們裝飾的目標抽象類。FileInputstream就是一個ConcreteComponent,即待裝飾的具體對象,它并不是JAVA的IO結構中的一個裝飾器,因為它無法裝飾InputStream。剩下BufferedInputStream,DataInputstream等等就是各種裝飾器了,對比上述的標準裝飾器樣板,JAVA的IO中也有抽象的裝飾器基類的存在,只是上述沒有體現出來,就是FilterInputStream,它是很多裝飾器最基礎的裝飾基類。
???????????????????? 在上述過程中,其中dataInputStream是經過兩次裝飾后得到的,它具有了dataInputStream和bufferedInputStream的雙重功能,另外,InputStreamReader是一個特殊的裝飾器,它提供了字節流到字符流的橋梁,其實它除了具有裝飾器的特點以外,也有點像一個適配器,但LZ還是覺得它應當算是一個裝飾器。
????????????????????其它的IO裝飾器各位可以自行嘗試或者和上述的標準的裝飾器模式代碼比對一下,下面另附LZ的IO裝飾器程序運行后結果。
?????????????????????從上面的展示中,已經可以充分體會到裝飾器模式的靈活了,我們創建的一個FileInputstream對象,我們可以使用各種裝飾器讓它具有不同的特別的功能,這正是動態擴展一個類的功能的最佳體現,而裝飾器模式的靈活性正是JAVA中IO所需要的,不得不贊一下JAVA類庫的建造者實在是強悍。
?????????????????????上述的XXXXInputStream的各個類都繼承了InputStream,這樣做不僅是為了復用InputStream的父類功能(InputStream也是一種模板方法模式,它定義了read(byte[])方法的簡單算法,并將read()方法交給具體的InputStream去實現),也是為了可以重疊裝飾,即裝飾器也可以再次被裝飾,而過渡到Reader以后,Reader的裝飾器體系則是類似的。
? ? ? ? ? ? ? ? ? ? ?下面LZ給出上面IO包中所涉及的類的類圖,各位可以自行和上面的標準裝飾器模式對比一下。
? ? ? ? ? ? ? ? ? ? ?LZ在類圖上標注了各個類負責的角色,并且使用背景顏色將InputStream和Reader體系分開,其中左半部分就是InputStream的裝飾體系,右半部分就是Reader的裝飾體系,并且他們之間的橋梁是InputStreamReader,他們每一個裝飾體系都與上面標準的裝飾器模式類圖極其相似,各位可以自己看一下,感受一下,尤其是InputStreamReader,它的位置比較特殊。
?????????????????????總之呢,裝飾器模式就是一個可以非常靈活的動態擴展類功能的設計模式,它采用組合的方式取代繼承,使得各個功能的擴展更加獨立和靈活。
?????????????????????本次裝飾器模式就到此結束了,感謝各位的收看,下期再見。
from:?https://blog.csdn.net/zuoxiaolong8810/article/details/9123533
總結
以上是生活随笔為你收集整理的(十)装饰器模式详解(与IO不解的情缘)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。