裝飾者模式
Decorator模式(別名Wrapper):動態將職責附加到對象上,若要擴展功能,裝飾者提供了比繼承更具彈性的代替方案。
意圖:
動態地給一個對象添加一些額外的職責。就增加功能來說,Decorator模式相比生成子類更爲靈活。
設計原則:
1. 多用組合,少用繼承。
利用繼承設計子類的行爲,是在編譯時靜態決定的,而且所有的子類都會繼承到相同的行爲。然而,如果能夠利用組合的做法擴展對象的行爲,就可以在運行時動態地進行擴展。
2. 類應設計的對擴展開放,對修改關閉。
要點:
1. 裝飾者和被裝飾對象有相同的超類型。
2. 可以用一個或多個裝飾者包裝一個對象。
3. 裝飾者可以在所委託被裝飾者的行爲之前或之後,加上自己的行爲,以達到特定的目的。
4. 對象可以在任何時候被裝飾,所以可以在運行時動態的,不限量的用你喜歡的裝飾者來裝飾對象。
5. 裝飾模式中使用繼承的關鍵是想達到裝飾者和被裝飾對象的類型匹配,而不是獲得其行爲。
6. 裝飾者一般對組件的客戶是透明的,除非客戶程序依賴於組件的具體類型。在實際項目中可以根據需要爲裝飾者添加新的行爲,做到“半透明”裝飾者。
7. 適配器模式的用意是改變對象的接口而不一定改變對象的性能,而裝飾模式的用意是保持接口並增加對象的職責。
實現:
Component:
定義一個對象接口,可以給這些對象動態地添加職責。
public interface Component
{
void operation();
}
Concrete Component:
定義一個對象,可以給這個對象添加一些職責。
public class ConcreteComponent implements Component
{
public void operation()
{
// Write your code here
}
}
Decorator:
維持一個指向Component對象的引用,並定義一個與 Component接口一致的接口。
public class Decorator implements Component
{
public Decorator(Component component)
{
this.component = component;
}
public void operation()
{
component.operation();
}
private Component component;
}
Concrete Decorator:
在Concrete Component的行爲之前或之後,加上自己的行爲,以“貼上”附加的職責。
public class ConcreteDecorator extends Decorator
{
public void operation()
{
//addBehavior也可以在前面
super.operation();
addBehavior();
}
private void addBehavior()
{
//your code
}
}
模式的簡化:
1. 如果只有一個Concrete Component類而沒有抽象的Component接口時,可以讓Decorator繼承Concrete Component。
2. 如果只有一個Concrete Decorator類時,可以將Decorator和Concrete
Decorator合併。
適用性:
以下情況使用Decorator模式
1. 需要擴展一個類的功能,或給一個類添加附加職責。
2. 需要動態的給一個對象添加功能,這些功能可以再動態的撤銷。
3. 需要增加由一些基本功能的排列組合而產生的非常大量的功能,從而使繼承關係變的不現實。
4. 當不能採用生成子類的方法進行擴充時。一種情況是,可能有大量獨立的擴展,爲支持每一種組合將產生大量的子類,使得子類數目呈爆炸性增長。另一種情況可能是因爲類定義被隱藏,或類定義不能用於生成子類。
優點:
1. Decorator模式與繼承關係的目的都是要擴展對象的功能,但是Decorator可以提供比繼承更多的靈活性。
2. 通過使用不同的具體裝飾類以及這些裝飾類的排列組合,設計師可以創造出很多不同行爲的組合。
缺點:
1. 這種比繼承更加靈活機動的特性,也同時意味着更加多的複雜性。
2. 裝飾模式會導致設計中出現許多小類,如果過度使用,會使程序變得很複雜。
3. 裝飾模式是針對抽象組件(Component)類型編程。但是,如果你要針對具體組件編程時,就應該重新思考你的應用架構,以及裝飾者是否合適。當然也可以改變Component接口,增加新的公開的行爲,實現“半透明”的裝飾者模式。在實際項目中要做出最佳選擇。
裝飾模式在Java I/O庫中的應用:
編寫一個裝飾者把所有的輸入流內的大寫字符轉化成小寫字符:
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
public class LowerCaseInputStream extends FilterInputStream
{
protected LowerCaseInputStream(InputStream in)
{
super(in);
}
@Override
public int read() throws IOException
{
int c = super.read();
return (c == -1 ? c : Character.toLowerCase((char) c));
}
@Override
public int read(byte[] b, int offset, int len) throws IOException
{
int result = super.read(b, offset, len);
for (int i = offset; i < offset + result; i++)
{
b[i] = (byte) Character.toLowerCase((char) b[i]);
}
return result;
}
}
測試我們的裝飾者類:
import java.io.*;
public class InputTest
{
public static void main(String[] args) throws IOException
{
int c;
try
{
InputStream in = new LowerCaseInputStream(new BufferedInputStream(
new FileInputStream("D:\\test.txt")));
while ((c = in.read()) >= 0)
{
System.out.print((char) c);
}
in.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}