废话:
预料中的日志3暂时生产不出来,话说难产就好,别夭折就行,有点掉价哦,呵呵。
因为有些东西还没有想清楚。那就先搞个四吧,这个东西还是清楚那么一点的。
一版描述:
项目需求 |
使每个servlet能对用户访问权限进行检查。简单来说,就是要给每个servlet加个锁,有钥匙的用户才能访问。 |
而项目中用户所谓的访问权限是基于他拥有的角色。也就是说,servlet对用户访问权限的检查,就是对他所拥有角色的检查。暂时,每个用户只能拥有一个角色。
项目的角色很多,但是在web端暂时只有如下的三种:
既然这样,servlet的加锁方式就有:
servlet加锁方式 |
游客,学生,教师, 游客或学生,游客或教师,学生或教师, 游客或学生或教师 |
注:上面的“游客”就是“游客角色有权访问”的意思,依此类推。
这里只有关系“或”(||),如果一个servlet的加锁方式是“学生或教师”,也就是说拥有学生或教师角色的用户都可以访问它。关系“与”(&&)在这里不太可能存在,因为没有需求说:某个servlet一定只能由“既是学生又是教师的用户”才能访问,而且前面也说了,暂时一个用户“有且只有”一个角色。
So we can get following function:
“加锁方式数” = 2的“角色数”次方 - 1
“加锁方式数”是关于“角色数”的指数函数,也就是说它是关于“角色数”成“指数级”增长的,应该说很快了吧。
3个角色就有2的3次方-1个,也就是7个加锁方式。
|
运用OO的最基本方式,就是封装对象。既然有为servlet“看门”的责任,那就把这个责任封装成一个对象,用个俗名:validator。
接着,运用共性和个性的分析方法,既然有那么多种不同的看门方式(加锁方式),那就搞一个接口,然后让各种“不同”都实现这个接口,形成子类。那就有下面的图:
可以看到,由于有7个加锁方式,那就要有7个子类。每个子类根据自己逻辑override接口的validate方法。
这样,对于一个servlet,想让它上什么样的锁,只要让它拿到对应的子类的引用即可,如下图中的ClientServlet,我们规定只能有“学生或教师”才能访问它。它的部分代码便是:
1//new对应的Validator接口的子类。
2//这里是学生或教师可访问,因此要new Student_Or_Teacher_Validator
3Validator validator = new Student_Validator();
4//然后调用验证方法就可以了
5boolean ok = validator.validate();
至此,第一个解决方案就出来了。
思考:
不足 |
validator接口的子类数目随“角色数”成“指数级”增长,数量太多;而且子类中重复逻辑的代码很多,如“Student_Or_Teacher_Validator”就重复了“Student_Validator”和“Teacher_Validator”的逻辑,万一“Student_Validator”的逻辑要改,只要涉及Student的子类都要跟着改,维护上不方便。 |
进一步改进的可能 |
“Student_Or_Teacher_Validator类”的validate方法很大程度上就是“Student_Validator类”的validate方法和“Teacher_Validator类”的validate方法“或操作”出来的结果。
因此,能不能考虑由“Student_Validator类的validate方法”和“Teacher_Validator的validate方法”动态的构造一个功能如“Student_Or_Teacher_Validator类的validate方法”。
这样,“Student_Or_Teacher_Validator”就可以省略了,只剩下一些原子的“角色类”。要实现Student_Or_Teacher_Validator的验证功能,拿Student_Validator和Teacher_Validator装配一下就可以了。 |
结论,需要根据实际情况,动态的改变(装配)“Validator接口对象”的validate方法。
第一个火花就是“装饰模式”。它可以让客户端动态的组装对象的方法。真神奇!
第二版来啦
注:上图中出现了AndRelation和And的一系列角色类,可以暂时省略不看,因为前面说了,现在还没有“与”关系这个需求。只看Or的就可以。
我喜欢叫这个模式为洋葱模式,一层包一层,最外层对象某方法的逻辑是由内部一层一层对象的同一方法组合出来的。
使用了这个模式,便不用如一版那样实现那么多子类,只要实现几个“角色类”即可,这里有3个(学生角色:Or_Student、教师角色:Or_Teacher、游客角色:Or_Guest)。所有一版中别的子类都可以由这3个组装出来。
如要生成一版中的Student_Or_Teacher_Validator对象,可以用Or_Student和Or_Teacher两个对象“Or”出来:
1Validator validator = new Or_Student(new Or_Teacher(OrRelation(req)));
如要生成一版中的Guest_Or_Student_Or_Teacher_Validator对象,可以用Or_Student、Or_Teacher和Or _Guest三个对象“Or”出来:
1Validator validator = new Or_Student(new Or_Teacher(new Or_Guest(OrRelation(req))));
这种一层包一层的new方式,是不是很像洋葱?第一次看是很不习惯,看多了就觉得习惯了。
对客户端的影响:
一版中,客户端要什么样的验证类,就直接使用具体类。
二版中,客户端要什么样的验证类,它的工作多了那么一丁点,它需要先组装一下,正如上面的例子。这种组装的方法很易于理解和使用,不会给客户端带来任何的不便。如果实在觉得客户端组装不出来(傻B客户端),也可以搞个工厂给它supply!
优点:
相对一版最明显的优点就是类的数目少了很多。
一版不是说“指数级”吗?这里只是线性的了。假设某一天系统拓展到有10个角色,一版就有2的10次方那么多个,也就是1024个验证类。
而二版还是10个角色类,别的都可以在客户端使用的时候,动态的组装
更重要的是代码结构好了,重复逻辑少了。每个逻辑都以最atomic的大小放到最应该的地方。
进而,维护的代价少多了。如某天“教师角色”的验证逻辑发生了变化,只要改动Or_Teacher一个地方即可。
MARCO ZHANG 2006年2月18日23:49:56