Java IO-InputStream家族 -裝飾者模式

看到一篇文章介紹Java IO的設計,感受講的太好了,忍不住想要轉過來,原網址看起來會更舒服java

Java InputStream家族的設計設計模式

Java IO-InputStream家族 -裝飾者模式 陳霖 育樹霖瘋 2017-05-16 在學習java.io.*包的時候,Input-Stream那一羣類很讓人反感,子類繁多就不用說,使用起來很是奇怪。咱們想以緩存的方式從文件中讀取字節流。總要先建立一個FileInput-Stream,而後把這個File-InputStream放入Buffered-InputStream構造函數中去建立BufferedInputStre-am。完成這些工做後才能開始讀取文件。數組

(以緩存方式從文件中讀取數據輸入流的JAVA代碼)緩存

爲何咱們不能直接建立以緩存方式從文件中讀取數據的輸入流類呢?今天我帶着這樣的問題走進InputStream的家,但願他可以給我答案。函數

我:老人家,我想問問,爲何咱們想以帶緩存的方式從文件中讀取字節流須要建立FileInputStream和BufferedInput-Stream兩個類,這麼麻煩的實現方式您是怎麼想到的?這種設計是否是太缺心眼了?學習

InputStream:年輕人,這要從好久好久之前提及。那時候Java剛剛被創造出來,我也幸運的被創造出來。那時候我尚未任何子孫,孤家寡人一個,無所事事。一天,有一個叫小霍的年輕人找到了我。他說他要讓我飛黃騰達,子孫滿堂。線程

我:這麼神?那您講講您和小霍之間的故事吧。設計

時光倒流回20年前,小霍初見InputStream。對象

小霍:InputStream你好,我是JAVA帝國計劃生育委員會的工做人員,今天我帶着任務而來,組織決定讓您兒孫滿堂。繼承

InputStream:此話當真?

小霍:我小霍是誰啊,必須比啊。而了實現這個目標,我已經從計劃生育委員會給你爭取了好幾個生育名額。

InputStream:我知道從那爭取到名額很不容易,那你說說,組織準備讓我生幾個?

小霍:你是IO的輸入類,負責讀取數據(字節流)。數據就是你的包裹,你通常從哪些渠道得到包裹?

InputStream:文件,字節數組,StringBuffer,其它線程,對了還有已經被序列化的對象。

小霍:那你先根據數據來源的渠道生5個孩子,老大叫FileInputStream處理文件,老二叫ByteArrayInputStream處理字節數組,老三叫StringBufferInputStream處理StringBuffer,老四叫PipedInput-Stream處理線程間的輸入流,老五叫ObjectInputStream處理被序列化的對象。

InputStream:萬一有一個包裹裏面有多個或者多種數據輸入流呢。

小霍:那就再生一個SequenceInputStre-am處理一個包裹裏有多種數據來源的業務。還有其它問題嗎?沒問題我就回單位了。帝國剛創建,咱們計劃生育委員會掌管着全國的生育名額,我還忙着呢。你抓緊時間生孩子,有問題再找我。

InputStream:好咧,我這就關燈造人。

交流完畢後,小霍走了,我也抓緊時間把我6個孩子生了出來,爲國家作貢獻。In-putStream的6個孩子齊心合力,處理了JAVA早期不少的輸入業務。可是他們也面臨了新的問題。沒過多久。年輕的計生委員小霍再次找到了InputStream。

小霍:你那6孩子都是能人啊,可是如今客戶抱怨他們的工做還不夠到位。

InputStream:我那6個孩子個個工做勤勤懇懇,怎麼還不到位?

小霍:客戶嘛,都比較挑剔。他們抱怨大家讀取數據太慢了,尤爲是你的老大FileInputStream每次讀數據都慢死了。好多客戶等待都超過了幾十秒了,還沒把數據等回來。

InputStream:幾十秒很慢嗎?

小霍:咱們計算機都是以納秒計時的,所謂世間一秒鐘,機器已千年。那些客戶頭髮都等白了。

InputStream: 讀數據慢能怪我嗎?這不是硬盤慢形成的嗎?

小霍:是,是硬盤形成的,咱們想一個辦法讓用戶減小訪問硬盤的次數。好比建一個buffer怎麼樣?用戶須要的數據先讓他們在buffer裏面找,能找到就直接從buffer裏返回,實在找不到再去硬盤裏找。Buffer在內存裏,內存可比硬盤快10萬倍呢(內存在隨機訪問的速度上是硬盤的10萬倍以上)。

InputStream:這辦法好。客戶抱怨的其餘問題呢?

小霍:客戶想要的數據類型都是int, long, String這樣的JAVA基本類型,而你提供給他們的都是byte類型,還須要客戶本身進行類型轉換。客戶以爲麻煩。還有一個問題,stream裏面讀出來的數據就不能從新放回stream,客戶想要一個功能,能把讀出來的數據再推回stream裏面。

InputStream:看來我得再生3個孩子: 擁有緩存的BufferedInputStream,把byte轉換成JAVA基本類型的DataInput-Stream和回寫數據到stream的Push-backInput-Stream。

小霍:老夥計,你糊塗了,不止3個。你想一想,你已經有6個孩子。他們掌握着6種數據來源。若是每個數據來源都分別實現這3個新功能。你至少得生18個孩子吧。還有更大的問題,萬一客戶既想有數據回寫,又想要輸出int,long,String這樣的數據,還有要緩存。或者有的客戶只須要這3個新功能的其中2個呢?那麼一個數據源須要7個新類,6兄弟一共是42個。

InputStream:看來咱們得想象另外的辦法。這樣會形成類爆炸。再說讓我一下生那麼多孩子,我也煎熬啊。

小霍:你玩過俄羅斯套娃沒?一個實心的娃娃被各類各樣娃娃外殼套着。一個實心娃娃先套一個學生的外殼,那麼他就是學生了,若是我再在外面套一個運動員的殼,那麼他就成了有運動員身份的學生。咱們模仿這種形式,好比最裏面的實心娃娃是處理文件讀取的FileInputStream,外面套一個BufferedInputStream的殼,那麼這個套娃就是帶buffer的FileInput-Stream。若是再套一個DataInputStre-am,那麼套娃就成了能輸出int這樣java 基本類型而且帶buffer的FileInputStre-am。搭配由客戶去決定,咱們只須要提供套娃殼(新的3個功能類)和最裏面的實心娃InputStream(InputStream的6個孩子)。客戶在搭配套娃的時候必須有一個實心娃娃,不然就沒有數據來源。

(套娃圖)

InputStream:這很巧妙,那如何實現這樣一種設計呢?

小霍:有2個關鍵點:

1,既然套娃中必定有實心娃娃,那麼套娃的殼的類必須包含一個實心娃。好比BufferedInputStream裏面要包含一個InputStream,咱們經過BufferedInput-Stream的構造函數把這個實心娃娃Input-Stream放進去,固然DataInput-Stream和PushbackInputStream也同樣。

2,BufferedInputStream+實心娃娃InputStream組成的新套娃又能夠看成DataInputStream的實心娃娃,那麼咱們讓這些套娃的外殼BufferedInputStre-am,DataInputStream,BufferedInputStream都繼承自InputStream類,這樣才能實現新組成的套娃又能夠被另外的套娃殼嵌套。這3個套娃殼有着共同的特色都是裝飾實心娃娃,咱們再在他們上層在抽象一個FilterInputStream,讓FilterInput-Stream繼承自InputStream,讓Filter-InputStream裏面包含一個實心娃娃In-putStream。之後全部的裝飾類都從FilterInputStream繼承。

InputStream:這樣我也省事了,只須要再多生一個FilterInputStream,剩下的BufferedInputStream,DataInputStream,PushbackInputStream這樣的裝飾類都讓FilterInputStream去生了。

小霍:對,加上FilterInputStream,你就有7個孩子了,跟葫蘆娃同樣。前面6個哥哥都是和數據源有關,7弟就是用來裝飾6個哥哥。把數據源的InputStream類和裝飾的InputStream整合在了一塊兒。

InputStream:並且對於BufferedInput-Stream,DataInputStream,PushbackInputStream來講,我仍是爺爺,想着他們叫我爺爺的樣子,我內心就美滋滋的。

小霍:美的你,Java中不管多少次繼承都是子類父類關係,沒有爺爺這麼一說。我把家譜給你。你兒子太多,我就畫Byte-ArrayInputStream,FileInputStream 和FilterInputStream的簡化版。

(裝飾者模式類圖)

小霍:這樣的設計既避免了類爆炸,又可讓用戶本身去搭配核心類和裝飾類。並且也知足了一個重要的設計原則:開閉原則。這是指導思想。所謂開閉原則就是要對擴展開放,對修改關閉。咱們的目標是容許類很容易的進行擴展,在不修改代碼的狀況下就能夠搭配新的行爲。至於缺點嘛,在實例化的時候用戶不只僅實例化核心類,還要把核心類包進裝飾者中。對於初次接觸IO類庫的人,沒法輕易理解。

InputStream:這是一個好的設計模式,只是須要一些學習成本。之後要有人不理解這設計模式,我就把我和你之間的故事給他講一遍。

小霍:甚好。

時光回到如今.

InputStream:故事講完了,這下你明白了嗎?

我:原來是這樣啊,要是按照我起初的想法,有一個專門的類來處理個人InputFile-Stream+BufferedInputStream,那Input-Stream你早就由於類太多引發爆炸了。小霍是個厲害的人物啊。

InputStream:是啊,年輕人就的多學習。小霍確實是個了不得的人物。

結束語:有人問過關於文章裏說起的人物真實存在嗎?其實大多數都是我杜撰的。而本文中的小霍確有其人。亞瑟.範.霍夫(Arthurvan Hoff)早期傑出的Java工程師,大多數的Java.io類都出自他手。後來也擔任過Flipboard,Dell公司CTO.謝謝他把這麼精彩的設計帶到人間。文中說起的全部類InputFileStream,Buffered-InputStream等均可以在java.io.*中找到,有興趣的能夠去讀讀源碼,jdk的源碼就是最規範的java規範,最詳細的文檔。

相關文章
相關標籤/搜索