裝飾(Decorator)模式又名包裝(Wrapper)模式。Decorator以對客戶端透明的方式擴展對象的功能,是繼承的一種代替方案。
1.何時使用html
模擬類圖:java
在裝飾模式中的各個角色有:web
看headFirst中星巴茲咖啡的使用 設計模式
寫下星巴茲的代碼緩存
先從Beverage類下手,這不須要改變星巴茲原始的設計。以下app
public abstract class Beverage { String description = "Unknown Beverage"; public String getDescription() { return description; } public abstract double cost(); }
Beverage是一個抽象類,有兩個方法:getDescription()及cost()。ide
getDescription()已經在此實現了,可是cost()必須在子類中實現。函數
Beverage很簡單。讓咱們也來實現Condiment(調料)抽象類,也就是裝飾者類吧:測試
public abstract class CondimentDecorator extends Beverage { public abstract String getDescription(); }
1.首先,必須讓Condiment Decorator可以取代Beverage,因此將CondimentDecorator擴展自 Beverage 類。this
寫飲料的代碼
如今,已經有了基類,讓咱們開始開始實現一些飲料吧!先從濃縮咖啡(Espresso)開始。別忘了,咱們須要爲具體的飲料設置描述,並且還必須實現cost()方法。
//首先,讓Espresso擴展自Beverage類,由於Espresso是一種飲料。 public class Espresso extends Beverage { //爲了要設置飲料的描述,咱們寫了一個構造器。記住,description實例變量繼承自Beverage。 public Espresso() { description = "Espresso"; } //最後,須要計算Espresso的價錢,如今不須要管調料的價錢,直接把Espresso的價格$1.99返回便可。 public double cost() { return 1.99; } } public class HouseBlend extends Beverage { public HouseBlend() { description = "House Blend Coffee"; } //這是另外一種飲料,作法和Espresso同樣,只是把Espresso名稱改成"House Blend Coffee",並返回正確的價錢$0.89。 public double cost() { return .89; } }
寫調料代碼
若是你回頭去看看裝飾者模式的類圖,將發現咱們已經完成了抽象組件(Beverage),有了具體組件(HouseBlend),也有了抽象裝飾者(CondimentDecorator)。如今,咱們就來實現具體裝飾者。先從摩卡下手:
//摩卡是一個裝飾者,因此讓它擴展自CondimentDecorator。 public class Mocha extends CondimentDecorator {//別忘了,CondimentDecorator擴展自Beverage。 Beverage beverage; public Mocha(Beverage beverage) { //要讓Mocha可以引用一個Beverage,作法以下:(1)用一個實例變量記錄飲料,也就是被裝飾者。 //(2)想辦法讓被裝飾者(飲料)被記錄到實例變量中。這裏的作法是:把飲料看成構造器的參數,再由構造器將此飲料記錄在實例變量中。 this.beverage = beverage; } public String getDescription() { //咱們但願敘述不僅是描述飲料(例如「DarkRoast」),而是完整地連調料都描述出來(例如「DarkRoast, Mocha」)。 //因此首先利用委託的作法,獲得一個敘述,而後在其後加上附加的敘述(例如「Mocha」)。 return beverage.getDescription() + ", Mocha"; } public double cost() { //要計算帶Mocha飲料的價錢。首先把調用委託給被裝飾對象,以計算價錢,而後再加上Mocha的價錢,獲得最後結果。 return .20 + beverage.cost(); } }
public class StarbuzzCoffee { public static void main(String args[]) { Beverage beverage = new Espresso(); System.out.println(beverage.getDescription()+ " $" + beverage.cost());//訂一杯Espresso,不須要調料,打印出它的描述與價錢 Beverage beverage2 = new DarkRoast();//製造出一個DarkRoast對象。 beverage2 = new Mocha(beverage2);//用Mocha裝飾它。 beverage2 = new Mocha(beverage2);//用第二個Mocha裝飾它。 beverage2 = new Whip(beverage2);//用Whip裝飾它。 System.out.println(beverage2.getDescription() + " $" + beverage2.cost()); Beverage beverage3 = new HouseBlend(); //最後,再來一杯調料爲豆漿、摩卡、奶泡的HouseBlend咖啡。 beverage3 = new Soy(beverage3); beverage3 = new Mocha(beverage3); beverage3 = new Whip(beverage3); System.out.println(beverage3.getDescription() + " $" + beverage3.cost()); } }
結果
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; } ... }
而且覆蓋了父類的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); } } }
它從FilterOutputStream繼承,而且重寫了它的write(int b)方法。在write(int b)方法中首先對輸入字符進行了檢查,若是不是空格,則輸出。
如下是一個測試程序:
import java.io.BufferedInputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; /** * Test the SkipSpaceOutputStream. * @author Magic * */ public class Test { public static void main(String[] args){ byte[] buffer = new byte[1024]; /** * Create input stream from the standard input. */ InputStream in = new BufferedInputStream(new DataInputStream(System.in)); /** * write to the standard output. */ OutputStream out = new SkipSpaceOutputStream(new DataOutputStream(System.out)); try { System.out.println("Please input your words: "); int n = in.read(buffer,0,buffer.length); for(int i=0;i〈n;i++){ out.write(buffer[i]); } } catch (IOException e) { e.printStackTrace(); } } }
執行以上測試程序,將要求用戶在console窗口中輸入信息,程序將過濾掉信息中的空格,並將最後的結果輸出到console窗口。好比:
Please input your words:
a b c d e f
abcdef
總 結
在java.io包中,不只OutputStream用到了Decorator設計模式,InputStream,Reader,Writer等都用到了此模式。而做爲一個靈活的,可擴展的類庫,JDK中使用了大量的設計模式,好比在Swing包中的MVC模式,RMI中的Proxy模式等等。對於JDK中模式的研究不只能加深對於模式的理解,並且還有利於更透徹的瞭解類庫的結構和組成。
Activity組件經過其父類ContextThemeWrapper和ContextWrapper的成員變量mBase來引用了一個ContextImpl對象,這樣,Activity組件之後就能夠經過這個ContextImpl對象來執行一些具體的操做,例如,啓動Service組件、註冊廣播接收者和啓動Content Provider組件等操做。同時,ContextImpl類又經過本身的成員變量mOuterContext來引用了與它關聯的一個Activity組件,這樣,ContextImpl類也能夠將一些操做轉發給Activity組件來處理。