日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

(十)装饰器模式详解(与IO不解的情缘)

發布時間:2025/3/21 编程问答 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 (十)装饰器模式详解(与IO不解的情缘) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者: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);//裝飾成A??
  • ????????System.out.println("------------------------------");??
  • ????????concreteDecoratorA.method();//原來的方法??
  • ????????concreteDecoratorA.methodA();//裝飾成A以后新增的方法??
  • ????????ConcreteDecoratorB?concreteDecoratorB?=?new?ConcreteDecoratorB(component);//裝飾成B??
  • ????????System.out.println("------------------------------");??
  • ????????concreteDecoratorB.method();//原來的方法??
  • ????????concreteDecoratorB.methodB();//裝飾成B以后新增的方法??
  • ????????concreteDecoratorB?=?new?ConcreteDecoratorB(concreteDecoratorA);//裝飾成A以后再裝飾成B??
  • ????????System.out.println("------------------------------");??
  • ????????concreteDecoratorB.method();//原來的方法??
  • ????????concreteDecoratorB.methodB();//裝飾成B以后新增的方法??
  • ????}??
  • }??
  • ???????????????? 下面看下我們運行的結果,到底是產生了什么效果。

    ???????????????從此可以看到,我們首先是使用的原始的類的方法,然后分別讓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?{??
  • ??
  • ????/*?test.txt內容:?
  • ?????*?hello?world!?
  • ?????*/??
  • ????public?static?void?main(String[]?args)?throws?IOException,?ClassNotFoundException?{??
  • ????????//文件路徑可自行更換??
  • ????????final?String?filePath?=?"E:/myeclipse?project/POITest/src/com/decorator/test.txt";??
  • ??????????
  • ????????//InputStream相當于被裝飾的接口或者抽象類,FileInputStream相當于原始的待裝飾的對象,FileInputStream無法裝飾InputStream??
  • ????????//另外FileInputStream是以只讀方式打開了一個文件,并打開了一個文件的句柄存放在FileDescriptor對象的handle屬性??
  • ????????//所以下面有關回退和重新標記等操作,都是在堆中建立緩沖區所造成的假象,并不是真正的文件流在回退或者重新標記??
  • ????????InputStream?inputStream?=?new?FileInputStream(filePath);??
  • ????????final?int?len?=?inputStream.available();//記錄一下流的長度??
  • ????????System.out.println("FileInputStream不支持mark和reset:"?+?inputStream.markSupported());??
  • ??????????
  • ????????System.out.println("---------------------------------------------------------------------------------");??
  • ??????????
  • ????????/*?下面分別展示三種裝飾器的作用BufferedInputStream,DataInputStream,PushbackInputStream,LZ下面做了三個裝飾器的功能演示??*/??
  • ??????????
  • ????????//首先裝飾成BufferedInputStream,它提供我們mark,reset的功能??
  • ????????BufferedInputStream?bufferedInputStream?=?new?BufferedInputStream(inputStream);//裝飾成?BufferedInputStream??
  • ????????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,又使用BufferedInputStream的mark?reset功能,所以我們再進行一層包裝??
  • ????????//注意,這里如果不使用BufferedInputStream,而使用原始的InputStream,read方法返回的結果會是-1,即已經讀取結束??
  • ????????//因為BufferedInputStream已經將文本的內容讀取完畢,并緩沖到堆上,默認的初始緩沖區大小是8192B??
  • ????????DataInputStream?dataInputStream?=?new?DataInputStream(bufferedInputStream);??
  • ????????dataInputStream.reset();//這是BufferedInputStream提供的功能,如果不在這個基礎上包裝會出錯??
  • ????????System.out.println("DataInputStream現在具有readInt,readChar,readUTF等功能");??
  • ????????int?value?=?dataInputStream.readInt();//讀出來一個int,包含四個字節??
  • ????????//我們轉換成字符依次顯示出來,可以看到LZ文件的前四個字符??
  • ????????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("---------------------------------------------------------------------------------");??
  • ??????????
  • ????????//PushbackInputStream無法包裝BufferedInputStream支持mark?reset,因為它覆蓋了reset和mark方法??
  • ????????//因為流已經被讀取到末尾,所以我們必須重新打開一個文件的句柄,即FileInputStream??
  • ????????inputStream?=?new?FileInputStream(filePath);??
  • ????????PushbackInputStream?pushbackInputStream?=?new?PushbackInputStream(inputStream,len);//裝飾成?PushbackInputStream??
  • ????????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];//清空byte數組??
  • ????????pushbackInputStream.read(bytes);//再讀??
  • ????????System.out.println("unread回退后的內容:"?+?new?String(bytes));??
  • ??????????
  • ????????System.out.println("---------------------------------------------------------------------------------");??
  • ??????????
  • ????????/*??以上有兩個一層裝飾和一個兩層裝飾,下面我們先裝飾成Reader,再進行其它裝飾???*/??
  • ??????????
  • ????????//由于之前被PushbackInputStream將流讀取到末尾,我們需要再次重新打開文件句柄??
  • ????????inputStream?=?new?FileInputStream(filePath);??
  • ????????InputStreamReader?inputStreamReader?=?new?InputStreamReader(inputStream,"utf-8");//先裝飾成InputStreamReader??
  • ????????System.out.println("InputStreamReader有reader的功能,比如轉碼:"?+?inputStreamReader.getEncoding());??
  • ??????????
  • ????????System.out.println("---------------------------------------------------------------------------------");??
  • ??????????
  • ????????BufferedReader?bufferedReader?=?new?BufferedReader(inputStreamReader);//我們進一步在reader的基礎上裝飾成BufferedReader??
  • ????????System.out.println("BufferedReader有readLine等功能:"?+?bufferedReader.readLine());??
  • ??????????
  • ????????System.out.println("---------------------------------------------------------------------------------");??
  • ??????????
  • ????????LineNumberReader?lineNumberReader?=?new?LineNumberReader(inputStreamReader);//我們進一步在reader的基礎上裝飾成LineNumberReader??
  • ????????System.out.println("LineNumberReader有設置行號,獲取行號等功能(行號從0開始),當前行號:"?+?lineNumberReader.getLineNumber());??
  • ??????????
  • ????????System.out.println("---------------------------------------------------------------------------------");??
  • ??????????
  • ????????//此處由于剛才被readLine方法將流讀取到末尾,所以我們再次重新打開文件句柄,并需要將inputstream再次包裝成reader??
  • ????????inputStreamReader?=?new?InputStreamReader(new?FileInputStream(filePath));??
  • ????????PushbackReader?pushbackReader?=?new?PushbackReader(inputStreamReader,len);//我們進一步在reader的基礎上裝飾成PushbackReader??
  • ????????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];//清空char數組??
  • ????????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不解的情缘)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。