01. 裝飾模式
1. 定義
Decorator裝飾器,就是動態地給一個對象添加一些額外的職責,動態擴展,和下面繼承(靜態擴展)的比較。因此,裝飾器模式具有如下的特征:
- 它必須持有一個被裝飾的對象(作為成員變量)。
- 它必須擁有與被裝飾對象相同的接口(多態調用、擴展需要)。
- 它可以給被裝飾對象添加額外的功能。
總結:保持接口,動態增強性能。
裝飾器通過包裝一個裝飾對象來擴展其功能,而又不改變其接口,這實際上是基于對象的適配器模式的一種變種。與對象的適配器模式異同:
- 相同點:都擁有一個目標對象。
- 不同點:適配器模式需要實現舊接口,而裝飾器模式必須實現相同接口。
適配器模式是在適配器中,重寫舊接口的方法來調用新接口方法,來實現舊接口不改變,同時使用新接口的目的。新接口適配舊接口。
而裝飾模式,是裝飾器和舊接口實現相同的接口,在調用新接口的方法中,會調用舊接口的方法,并對其進行擴展。
2. 由來(為什么不是繼承)
功能的拓展,通常可以使用繼承的方式解決。但這樣實現的話,每一種組合都需要一個類,大量重復性內容,類數目“爆炸”;另外,這些拓展的功能必須要是可以預見,編譯時就確定了,靜態的擴展。
一個例子大概說:Beverage是一個抽象類,它被所有在一個咖啡店里賣的飲料繼承。Beverage有個抽象方法cost,所有的子類都要實現這個抽象方法,計算它們的價格。現在有四個最基本的咖啡:HouseBlend,DarkRoast,Decaf,Espresso他們都繼承自Beverage,現在的需求是說在四個最基本的咖啡里,每個都可以隨便地添加調味品,像steamed milk,soy,還有mocha最后是加上whipped milk。如果是說按繼承來實現這種幾個調味品跟原來咖啡的組合的話,我們會很自然地設計來下面的類圖來:
如果是按裝飾模式的設計思路我們可以得出下面的設計類圖:
裝飾模式是怎么達到不僅類的數目大減少了,性能的重復也可以減至到最少。
3. 典型結構圖
一句話解釋:裝飾者和被裝飾者需要繼承同一個接口或者是抽象類,被裝飾者作為裝飾者的一個變量。程序中原來調用被裝飾者某方法func1的地方改成調用裝飾者相同的那個方法func1,并且裝飾者的該方法func1上添加了一些額外的功能,在方法func1中再調用被裝飾著的方法func1。
這就是動態的擴展。
02. 適配器模式
目的:將一個類的接口轉換成客戶期望的另一個接口,讓原本不兼容的接口可以合作無間。
1. 特點
- 適配器對象實現原有接口
- 適配器對象組合一個實現新接口的對象(這個對象也可以不實現一個接口,只是一個單純的對象)
- 對適配器原有接口方法的調用被委托給新接口的實例的特定方法(重寫舊接口方法來調用新接口功能。)
2.例子
我國國標充電器三孔,德國得標充電器兩孔。現去德國旅行。如何將我們的三孔充電器插入兩孔。這就需要適配器。
類圖:
DBSocketInterface:德標接口
DBSocket:德國插座(實現DBSocketInterface,提供兩孔充電方法)
Hotel : 擁有得標接口。
GBSocketInterface :國標接口
GBSocket : 中國插座(實現GBSocketInterface,提供三孔充電方法)
適配器實現:
public class SocketAdapter
implements DBSocketInterface{ //實現舊接口
//組合新接口
private GBSocketInterface gbSocket;
/**
* 在創建適配器對象時,必須傳入一個新街口的實現類
*/
public SocketAdapter(GBSocketInterface gbSocket) {
this.gbSocket = gbSocket;
}
/**
* 將對就接口的調用適配到新接口
*/
@Override
public void powerWithTwoRound() {
gbSocket.powerWithThreeFlat();
}
}
在適配器中,繼承了舊接口,組合了新接口。在重寫舊接口方法的時候,調用了新接口的功能。
hotel.setSocket(socketAdapter);
hotel.charge();
在hotel的charge()方法中,調用的是DBSocketInterface 實現子類的兩孔充電方法。而這個實現子類,就是適配器類,重寫的調用三孔充電的方法。
這也是適配器模式魅力:
不改變原有接口(德標),卻還能使用新接口的功能(國標)。
適配器詳情和實際使用例子參考《http://blog.csdn.net/zhangjg_blog/article/details/18735243》
03. Java IO 設計模式
1. I/O庫對稱性
- 輸入-輸出對稱:比如InputStream和OutputStream對Byte字節流的輸入和輸;而Reader和Writer各自占據Char字符流的輸入和輸出。
- byte-char對稱:InputStream和Reader的子類分別負責byte和字符流的輸入;OutputStream和Writer的子類分別負責byte和字符流的輸出。
2.兩個設計模式
- 裝飾模式:在由InputStream、OutputStream、Reader和Writer代表的等級結構內部,有一些流處理器可以對另一些流處理器起到裝飾作用,形成新的、具有改善了的功能的流處理器。
- 適配器模式:在由InputStream、OutputStream、Reader和Writer代表的等級結構內部,有一些流處理器是對其他類型的流處理器的適配。這就是適配器的應用。
3.裝飾模式的應用
由于java I/O庫需要很多性能的各種組合,如果這些性能都是用繼承來實現,那么每一種組合都需要一個類,大量重復類。
首先,需要理解java I/O庫是由一些原始流處理器和圍繞它的裝飾流處理器(裝飾器,動態擴展原始類性能)所組成的。
這里以InputStream為例,并附上結構圖。
這些流類分成兩種,即原始流類(Original Stream)和鏈接流處理器(Wrapper Stream)。
原始流處理器
原始流處理器接收一個Byte數組對象,String對象,FileDiscriptor對象或者不同類型的流源對象,原始流處理器包括以下四種:
- ByteArrayInputStream:接收一個Byte數組作為流的源。
- FileInputStream:建立一個與文件有關的輸入流。接收一個File對象作為流的源。
- PipedInputStream:可以與PipedOutputStream配合使用,用于讀入一個數據管道的數據,接收一個PipedOutputStream作為源。
- StringBufferInputStream:將一個字符串緩沖區轉換為一個輸入流。(廢棄)
鏈接流處理器
所謂鏈接流處理器,就是可以接收另一個流對象作為源,并對之進行功能擴展的類。InputStream類型的鏈接處理接收另一個InputStream對象作為流源。
以FilterInputStream過濾輸入流的子類為例。它將另一個輸入流作為流源。這個類的子類包括以下幾種:
- BufferedInputStream:用來從硬盤將數據讀入到一個內存緩沖區中,并從緩沖區提供數據。
- DataInputStream:提供基于多字節的讀取方法,可以讀取原始類型的數據。
- LineNumberInputStream:提供帶有行計數功能的過濾輸入流。
- PushbackInputStream:提供特殊的功能,可以將已經讀取的字節“推回”到輸入流中。
原始流,就是裝模式中具體構件角色(被裝飾者),鏈接流,就是裝飾模式中的裝飾角色。
抽象結構圖
一句話總結:鏈接流處理器,接收原始流處理器并將其作為成員變量引用,都繼承了相同的抽象類InputStream。他們在內部工作方法中做了相應改變,在鏈接流的相同方法中擴展原始流中的方法,這種變化就是裝飾模式的目的。
4.適配器模式的應用
StringBufferInputStream是一個適配器類,其繼承了InputStream類型,同時持有一個對String類型的引用。這是將處理String對象的新接口適配成InputStream的舊接口的適配器模式。
其余OutputStream、Reader和Writer的裝飾模式和適配器模式
參考《http://www.cnblogs.com/wxgblogs/p/5649933.html》
和《http://www.cnblogs.com/heartstage/p/3391070.html》
文章列表