首页 » 编写高质量代码:改善Java程序的151个建议 » 编写高质量代码:改善Java程序的151个建议全文在线阅读

《编写高质量代码:改善Java程序的151个建议》建议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上直接定义一个常量对象,那就是“赤裸裸”的委派了!