讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議107:使用反射增加裝飾模式的普適性 >

建議107:使用反射增加裝飾模式的普適性

裝飾模式(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();

}


此類代碼是一個比較通用的裝飾模式,只需要定義被裝飾的類及裝飾類即可,裝飾行為由動態代理實現,實現了對裝飾類和被裝飾類的完全解耦,提供了系統的擴展性。