讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 建議91:枚舉和註解結合使用威力更大 >

建議91:枚舉和註解結合使用威力更大

我們知道註解的寫法和接口很類似,都採用了關鍵字interface,而且都不能有實現代碼,常量定義默認都是public static final類型的等,它們的主要不同點是:註解要在interface前加上@字符,而且不能繼承,不能實現,這經常會給我們的開發帶來一些障礙。

我們來分析一個ACL(Access Control List,訪問控制列表)設計案例,看看如何避免這些障礙,ACL有三個重要元素:

資源,有哪些信息是要被控制起來的。

權限級別,不同的訪問者規劃在不同的級別中。

控制器(也叫鑒權人),控制不同的級別訪問不同的資源。

鑒權人是整個ACL的設計核心,我們從最主要的鑒權人開始,代碼如下:


interface Identifier{

//無權訪問時的禮貌語

String REFUSE_WORD="您無權訪問";

//鑒權

public boolean identify();

}


這是一個鑒權人接口,定義了一個常量和一個鑒權方法。接下來應該實現該鑒權方法,但問題是我們的權限級別和鑒權方法之間是緊耦合,若分拆成兩個類顯得有點囉嗦,怎麼辦?我們可以直接定義一個枚舉來實現,代碼如下:


enum CommonIdentifer implements Identifier{

//權限級別

Reader, Author, Admin;

//實現鑒權

public boolean identify(){

return false;

}

}


定義了一個通用鑒權者,使用的是枚舉類型,並且實現了鑒權者接口。現在就剩下資源定義了,這很容易定義,資源就是我們寫的類、方法等,之後再通過配置來決定哪些類、方法允許什麼級別的訪問,這裡的問題是:怎麼把資源和權限級別關聯起來呢?使用XML配置文件?是個方法,但對我們的示例程序來說顯得太繁重了,如果使用註解會更簡潔些,不過這需要我們首先定義出權限級別的註解,代碼如下:


@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

@interface Access{

//什麼級別可以訪問,默認是管理員

CommonIdentifier level()default CommonIdentifier.Admin;

}


該註解是標注在類上面的,並且會保留到運行期。我們定義一個資源類,代碼如下:


@Access(level=CommonIdentifier.Author)

class Foo{

}


Foo類只能是作者級別的人訪問。場景都定義完畢了,那我們看看如何模擬ACL的實現,代碼如下:


public static void main(Stringargs){

//初始化商業邏輯

Foo b=new Foo();

//獲取註解

Access access=b.getClass().getAnnotation(Access.class);

//沒有Access註解或者鑒權失敗

if(access==null||!access.level().identify()){

//沒有Access註解或者鑒權失敗

System.out.println(access.level().REFUSE_WORD);

}

}


看看這段代碼,簡單、易讀,而且如果我們是通過ClassLoader類來解釋該註解的,那會使我們的開發更加簡潔,所有的開發人員只要增加註解即可解決訪問控制問題。注意看加粗代碼,access是一個註解類型,我們想使用Identifier接口的identify鑒權方法和REFUSE_WORD常量,但註解是不能繼承的,那怎麼辦?此處,可通過枚舉類型CommonIdentifier從中間做一個委派動作(Delegate),委派?沒看到!你可以讓identify返回一個對象,或者在Identifier上直接定義一個常量對象,那就是「赤裸裸」的委派了!