裝飾模式Decorator

 

 

裝飾(Decorator)模式又名包裝(Wrapper)模式。Decorator以對客戶端透明的方式擴展對象的功能,是繼承的一種代替方案。

1.何時使用html

  1. 須要動態的擴展一個類,這些擴展也能夠動態的撤銷,並保持原有類的靜態定義的狀況。
  2. 須要增長由一些基本功能排列組合貳產生的很是強大的功能,並使繼承關係變得不實現,典型的Wrapper應用。

 

模擬類圖:java

 

 

 

在裝飾模式中的各個角色有:web

  • 抽象構件(Component)角色:給出一個抽象接口,以規範準備接收附加責任的對象。
  • 具體構件(Concrete Component)角色:定義一個將要接收附加責任的類。
  • 裝飾(Decorator)角色:持有一個構件(Component)對象的實例,並定義一個與抽象構件接口一致的接口。
  • 具體裝飾(Concrete Decorator)角色:負責給構件對象"貼上"附加的責任。

 看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組件來處理。

相關文章
相關標籤/搜索