java io設計模式分析

來源:互聯網
上載者:User
我想任何一本介紹模式的書在講到Decorator模式的時候不能不提到它的實際應用——在Java/IO庫裡面的應用,<<Java與模式>>這本書也不例外,有點不一樣的是,這本書在介紹的時候有個專題,是從兩個模式來看Java/IO庫,完這個專題後,個人感覺對Java/IO庫有了全新的認識同時也加深了Decorator模式跟Adapter適配器模式的理解,現和大家分享下這個在我看來很偉大的成果,同時說明下,以下大部分文字跟圖片是來自<<Java與模式>>這本書。  一。引子(概括地介紹Java的IO)  無論是哪種程式設計語言,輸入跟輸出都是重要的一部分,Java也不例外,而且Java將輸入/輸出的功能和使用範疇做了很大的擴充。它採用了流的機制來實現輸入/輸出,所謂流,就是資料的有序排列,而流可以是從某個源(稱為流源或Source of Stream)出來,到某個目的地(稱為流匯或Sink of Stream)去的。由流的方向,可以分成輸入資料流和輸出資料流,一個程式從輸入資料流讀取資料向輸出資料流寫資料。  如,一個程式可以用FileInputStream類從一個磁碟檔案讀取資料,如所示:  像FileInputStream這樣的處理器叫做流處理器,它就像流的管道一樣,從一個流源吸入某種類型的資料,並輸出某種類型的資料。上面這種叫做流的管道圖。  同樣道理,也可以用FileOutputStream類向一個磁碟檔案寫資料,如所示:  在實際應用這種機制並不沒有太大的用處,程式需要寫出地通常是非常結構化的資訊,因此這些byte類型的資料實際上是一些數值,文字,原始碼等。Java的I/O庫提供了一個稱做連結(Chaining)的機制,可以將一個流處理器跟另一個流處理器首尾相接,以其中之一的輸出為輸入,形成一個流管道的連結。  例如,DataInputStream流處理器可以把FileInputStream流對象的輸出當作輸入,將Byte類型的資料轉換成Java的原始類型和String類型的資料。如所示:  類似地,向一個檔案寫入Byte類型的資料不是一個簡單的過程。一個程式需要向一個檔案裡寫入的資料往往都是結構化的,而Byte類型則是原始類型。因此在寫的時候必須經過轉換。DataOutputStream流處理器提供了接收了未經處理資料類型和String資料類型,而這個流處理器的輸出資料則是Byte類型。也就是說DataOutputStream可以將來源資料轉換成Byte類型的資料,再輸出來。  這樣一來,就可以將DataOutputStream與FileOutputStream連結起來,這樣程式就可以將未經處理資料類型和String類型的來源資料寫入這個連結好的雙重管道裡面,達到將結構化資料寫到磁碟檔案裡面的目的,如所示:  這又是連結的所發揮的大作用。  流處理器所處理的流必定都有流源,而如果將流類所處理的流源分類的話,基本可以分成兩大類:  第一 數組,String,File等,這一種叫原始流源。  第二 同樣類型的流用做連結流類的流源,叫連結流源。  二 Java I/O庫的設計原則  Java語言的I/O庫是對各種常見的流源,流匯以及處理過程的抽象化。用戶端的Java程式不必知道最終的流源,流匯是磁碟上的檔案還是數組等;也不必關心資料是否經過緩衝的,可否按照行號讀取等處理的細節。  書中提到了,對於第一次見到Java/IO庫的人,無不因為這個庫的龐雜而感到困惑;而對於熟悉這個庫的人,而又常常為這個庫的設計是否得當而爭論不體。書的作者提出自己的意見,要理解Java I/O這個龐大而複雜的庫,關鍵是要掌握兩個對稱性跟兩個設計模式模式。  Java I/O庫具有兩個對稱性,它們分別是:  1 輸入-輸出對稱性,比如InputStream和OutputStream各自佔據Byte流的輸入與輸出的兩個平行的等級結構的根部。而Reader和Writer各自佔據Char流的輸入與輸出的兩個平行的等級結構的根部。  2 byte-char對稱,InputStream和Reader的子類分別負責Byte和Char流的輸入;OutputStream和Writer的子類分別負責Byte和Char流的輸出,它們分別形成平行的等級結構。  Java I/O庫的兩個設計模式:  Java的I/O庫總體設計是符合裝飾者模式(Decorator)跟適配器模式(Adapter)的。如前所述,這個庫中處理流的類叫做流類。引子裡所談到的FileInputStream,FileOutputStream,DataInputStream及DataOutputStream都是流處理器的例子。  1 裝飾者模式:在由InputStream,OutputStream,Reader和Writer代表的等級結構內部,有一些流處理器可以對另一些流處理器起到裝飾作用,形成新的,具有改善了的功能的流處理器。裝飾者模式是Java I/O庫的整體設計模式。這樣的一個原則是符合裝飾者模式的,如所示:  2 適配器模式:在由InputStream,OutputStream,Reader和Writer代表的等級結構內部,有一些流處理器是對其它類型的流源的適配。這就是適配器模式的應用,如所示。  適配器模式應用到了原始流處理器的設計上面,構成了I/O庫所有流處理器的起點。  JDK為程式員提供了大量的類庫,而為了保持類庫的可重用性,可擴充性和靈活性,其中使用到了大量的設計模式,本文將介紹JDK的I/O包中使用到的Decorator模式,並運用此模式,實現一個新的輸出資料流類。  Decorator模式簡介  Decorator模式又名封裝器(Wrapper),它的主要用途在於給一個對象動態添加一些額外的職責。與產生子類相比,它更具有靈活性。  有時候,我們需要為一個對象而不是整個類添加一些新的功能,比如,給一個文本區添加一個捲軸的功能。我們可以使用繼承機制來實現這一功能,但是這種方法不夠靈活,我們無法控制文本區加捲軸的方式和時機。而且當文本區需要添加更多的功能時,比如邊框等,需要建立新的類,而當需要組合使用這些功能時無疑將會引起類的爆炸。  我們可以使用一種更為靈活的方法,就是把文本區嵌入到捲軸中。而這個捲軸的類就相當於對文本區的一個裝飾。這個裝飾(捲軸)必須與被裝飾的組件(文本區)繼承自同一個介面,這樣,使用者就不必關心裝飾的實現,因為這對他們來說是透明的。裝飾會將使用者的請求轉寄給相應的組件(即調用相關的方法),並可能在轉寄的前後做一些額外的動作(如添加捲軸)。通過這種方法,我們可以根據組合對文本區嵌套不同的裝飾,從而添加任意多的功能。這種動態對對象添加功能的方法不會引起類的爆炸,也具有了更多的靈活性。  以上的方法就是Decorator模式,它通過給對象添加裝飾來動態添加新的功能。如下是Decorator模式的UML圖:  Component為組件和裝飾的公用父類,它定義了子類必須實現的方法。  ConcreteComponent是一個具體的組件類,可以通過給它添加裝飾來增加新的功能。  Decorator是所有裝飾的公用父類,它定義了所有裝飾必須實現的方法,同時,它還儲存了一個對於Component的引用,以便將使用者的請求轉寄給Component,並可能在轉寄請求前後執行一些附加的動作。  ConcreteDecoratorA和ConcreteDecoratorB是具體的裝飾,可以使用它們來裝飾具體的Component.  JAVA IO包中的Decorator模式  JDK提供的java.io包中使用了Decorator模式來實現對各種輸入輸出資料流的封裝。以下將以java.io.OutputStream及其子類為例,討論一下Decorator模式在IO中的使用。  首先來看一段用來建立IO流的代碼:  以下是程式碼片段:  try {  OutputStream out = new DataOutputStream(new FileOutputStream("test.txt"));  } catch (FileNotFoundException e) {  e.printStackTrace();  }  這段代碼對於使用過JAVA輸入輸出資料流的人來說再熟悉不過了,我們使用DataOutputStream封裝了一個FileOutputStream.這是一個典型的Decorator模式的使用,FileOutputStream相當於Component,DataOutputStream就是一個Decorator.將代碼改成如下,將會更容易理解:  以下是程式碼片段:  try {  OutputStream out = new FileOutputStream("test.txt");  out = new DataOutputStream(out);  } catch(FileNotFoundException e) {  e.printStatckTrace();  }  由於FileOutputStream和DataOutputStream有公用的父類OutputStream,因此對對象的裝飾對於使用者來說幾乎是透明的。下面就來看看OutputStream及其子類是如何構成Decorator模式的:  OutputStream是一個抽象類別,它是所有輸出資料流的公用父類,其原始碼如下:  以下是程式碼片段:  public abstract class OutputStream implements Closeable, Flushable {  public abstract void write(int b) throws IOException;  ……  }  它定義了write(int b)的抽象方法。這相當於Decorator模式中的Component類。  ByteArrayOutputStream,FileOutputStream 和 PipedOutputStream 三個類都直接從OutputStream繼承,以ByteArrayOutputStream為例:  以下是程式碼片段:  public class ByteArrayOutputStream extends OutputStream {  protected byte buf[];  protected int count;  public ByteArrayOutputStream() {  this(32);  }  public ByteArrayOutputStream(int size) {  if (size 〈 0) {  throw new IllegalArgumentException("Negative initial size: "  + size);  }  buf = new byte[size];  }  public synchronized void write(int b) {  int newcount = count + 1;  if (newcount 〉 buf.length) {  byte newbuf[] = new byte[Math.max(buf.length 〈〈 1, newcount)];  System.arraycopy(buf, 0, newbuf, 0, count);  buf = newbuf;  }  buf[count] = (byte)b;  count = newcount;  }  ……  }  它實現了OutputStream中的write(int b)方法,因此我們可以用來建立輸出資料流的對象,並完成特定格式的輸出。它相當於Decorator模式中的ConcreteComponent類。  接著來看一下FilterOutputStream,代碼如下:  以下是程式碼片段:  public class FilterOutputStream extends OutputStream {  protected OutputStream out;  public FilterOutputStream(OutputStream out) {  this.out = out;  }  public void write(int b) throws IOException {  out.write(b);  }  ……  }  同樣,它也是從OutputStream繼承。但是,它的建構函式很特別,需要傳遞一個OutputStream的引用給它,並且它將儲存對此對象的引用。而如果沒有具體的OutputStream對象存在,我們將無法建立FilterOutputStream.由於out既可以是指向FilterOutputStream類型的引用,也可以是指向ByteArrayOutputStream等具體輸出資料流類的引用,因此使用多層嵌套的方式,我們可以為ByteArrayOutputStream添加多種裝飾。這個FilterOutputStream類相當於Decorator模式中的Decorator類,它的write(int
b)方法只是簡單的調用了傳入的流的write(int b)方法,而沒有做更多的處理,因此它本質上沒有對流進行裝飾,所以繼承它的子類必須覆蓋此方法,以達到裝飾的目的。  BufferedOutputStream 和 DataOutputStream是FilterOutputStream的兩個子類,它們相當於Decorator模式中的ConcreteDecorator,並對傳入的輸出資料流做了不同的裝飾。以BufferedOutputStream類為例:  以下是程式碼片段:  public class BufferedOutputStream extends FilterOutputStream {  ……  private void flushBuffer() throws IOException {  if (count 〉 0) {  out.write(buf, 0, count);  count = 0;  }  }  public synchronized void write(int b) throws IOException {  if (count 〉= buf.length) {  flushBuffer();  }  buf[count++] = (byte)b;  }  ……  }  這個類提供了一個緩衝機制,等到緩衝的容量達到一定的位元組數時才寫入輸出資料流。首先它繼承了FilterOutputStream,並且覆蓋了父類的write(int b)方法,在調用輸出資料流寫出資料前都會檢查緩衝是否已滿,如果未滿,則不寫。這樣就實現了對輸出資料流對象動態添加新功能的目的。  下面,將使用Decorator模式,為IO寫一個新的輸出資料流。  自己寫一個新的輸出資料流  瞭解了OutputStream及其子類的結構原理後,我們可以寫一個新的輸出資料流,來添加新的功能。這部分中將給出一個新的輸出資料流的例子,它將過濾待輸出語句中的空格符號。比如需要輸出"java io OutputStream",則過濾後的輸出為"javaioOutputStream".以下為SkipSpaceOutputStream類的代碼:  以下是程式碼片段:  import java.io.FilterOutputStream;  import java.io.IOException;  import java.io.OutputStream;  /**  * A new output stream, which will check the space character  * and won‘t write it to the output stream.  * @author Magic  *  */  public class SkipSpaceOutputStream extends FilterOutputStream {  public SkipSpaceOutputStream(OutputStream out) {  super(out);  }  /**  * Rewrite the method in the parent class, and  * skip the space character.  */  public void write(int b) throws IOException{  if(b!=‘ ’){  super.write(b);
相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.