我們知道註解的寫法和接口很類似,都採用了關鍵字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上直接定義一個常量對象,那就是「赤裸裸」的委派了!