裝飾模式(Decorator Pattern)的定義是「動態地給一個對像添加一些額外的職責。就增加功能來說,裝飾模式相比於生成子類更為靈活」,不過,使用Java的動態代理也可以實現裝飾模式的效果,而且其靈活性、適應性都會更強。
我們以卡通片《貓和老鼠》(《Tom and Jerry》)為例,看看如何包裝小Jerry讓它更強大。首先定義Jerry的類:老鼠(Rat類),代碼如下;
interface Animal{
public void doStuff();
}
//老鼠是一種動物
class Rat implements Animal{
public void doStuff(){
System.out.println("Jerry will play with Tom.");
}
}
接下來我們要給Jerry增加一些能力,比如飛行、鑽地等能力,當然使用類繼承也很容易實現,但我們這裡只是臨時地為Rat類增加這些能力,使用裝飾模式更符合此處的場景。首先定義裝飾類,代碼如下:
//定義某種能力
interface Feature{
//加載特性
public void load();
}
//飛行能力
class FlyFeature implements Feature{
public void load(){
System.out.println("增加一隻翅膀……");
}
}
//鑽地能力
class DigFeature implements Feature{
public void load(){
System.out.println("增加鑽地能力……");
}
}
此處定義了兩種能力:一種是飛行,另一種是鑽地,我們如果把這兩種屬性賦予到Jerry身上,那就需要一個包裝動作類了,代碼如下:
class DecorateAnimal implements Animal{
//被包裝的動物
private Animal animal;
//使用哪一個包裝器
private Class<?extends Feature>clz;
public DecorateAnimal(Animal_animal, Class<?extends Feature>_clz){
animal=_animal;
clz=_clz;
}
@Override
public void doStuff(){
InvocationHandler handler=new InvocationHandler(){
//具體包裝行為
public Object invoke(Object p, Method m, Objectargs)throws
Throwable{
Object obj=null;
//設置包裝條件
if(Modifier.isPublic(m.getModifiers())){
obj=m.invoke(clz.newInstance(),args);
}
animal.doStuff();
return obj;
}
};
//當前加載器
ClassLoader cl=getClass().getClassLoader();
//動態代理,由Handler決定如何包裝
Feature proxy=(Feature)Proxy.newProxyInstance(cl, clz.
getInterfaces(),handler);
proxy.load();
}
}
注意看doStuff方法,一個裝飾類型必然是抽像構建(Component)的子類型,它必須要實現doStuff,此處的doStuff方法委託給了動態代理執行,並且在動態代理的控制器Handler中還設置了決定裝飾方式和行為的條件(即代碼中InvocationHandler匿名類中的if判斷語句),當然,此處也可以通過讀取持久化數據的方式進行判斷,這樣就更加靈活了。
抽像構件有了,裝飾類也有了,裝飾動作類也完成了,那我們就可以編寫客戶端進行調用了,代碼如下:
public static void main(Stringargs)throws Exception{
//定義Jerry這只家喻戶曉的老鼠
Animal Jerry=new Rat();
//為Jerry增加飛行能力
Jerry=new DecorateAnimal(Jerry, FlyFeature.class);
//Jerry增加挖掘能力
Jerry=new DecorateAnimal(Jerry, DigFeature.class);
//Jerry開始耍貓了
Jerry.doStuff();
}
此類代碼是一個比較通用的裝飾模式,只需要定義被裝飾的類及裝飾類即可,裝飾行為由動態代理實現,實現了對裝飾類和被裝飾類的完全解耦,提供了系統的擴展性。