廢話:
預(yù)料中的日志3暫時(shí)生產(chǎn)不出來,話說難產(chǎn)就好,別夭折就行,有點(diǎn)掉價(jià)哦,呵呵。
因?yàn)橛行〇|西還沒有想清楚。那就先搞個(gè)四吧,這個(gè)東西還是清楚那么一點(diǎn)的。
一版描述:
項(xiàng)目需求 |
使每個(gè)servlet能對用戶訪問權(quán)限進(jìn)行檢查。簡單來說,就是要給每個(gè)servlet加個(gè)鎖,有鑰匙的用戶才能訪問。 |
而項(xiàng)目中用戶所謂的訪問權(quán)限是基于他擁有的角色。也就是說,servlet對用戶訪問權(quán)限的檢查,就是對他所擁有角色的檢查。暫時(shí),每個(gè)用戶只能擁有一個(gè)角色。
項(xiàng)目的角色很多,但是在web端暫時(shí)只有如下的三種:
項(xiàng)目暫時(shí)角色 |
游客,學(xué)生,教師 |
既然這樣,servlet的加鎖方式就有:
servlet加鎖方式 |
游客,學(xué)生,教師, 游客或?qū)W生,游客或教師,學(xué)生或教師, 游客或?qū)W生或教師 |
注:上面的“游客”就是“游客角色有權(quán)訪問”的意思,依此類推。
這里只有關(guān)系“或”(||),如果一個(gè)servlet的加鎖方式是“學(xué)生或教師”,也就是說擁有學(xué)生或教師角色的用戶都可以訪問它。關(guān)系“與”(&&)在這里不太可能存在,因?yàn)闆]有需求說:某個(gè)servlet一定只能由“既是學(xué)生又是教師的用戶”才能訪問,而且前面也說了,暫時(shí)一個(gè)用戶“有且只有”一個(gè)角色。
So we can get following function:
“加鎖方式數(shù)” = 2的“角色數(shù)”次方 - 1
“加鎖方式數(shù)”是關(guān)于“角色數(shù)”的指數(shù)函數(shù),也就是說它是關(guān)于“角色數(shù)”成“指數(shù)級”增長的,應(yīng)該說很快了吧。
3個(gè)角色就有2的3次方-1個(gè),也就是7個(gè)加鎖方式。
|
運(yùn)用OO的最基本方式,就是封裝對象。既然有為servlet“看門”的責(zé)任,那就把這個(gè)責(zé)任封裝成一個(gè)對象,用個(gè)俗名:validator。
接著,運(yùn)用共性和個(gè)性的分析方法,既然有那么多種不同的看門方式(加鎖方式),那就搞一個(gè)接口,然后讓各種“不同”都實(shí)現(xiàn)這個(gè)接口,形成子類。那就有下面的圖:

可以看到,由于有7個(gè)加鎖方式,那就要有7個(gè)子類。每個(gè)子類根據(jù)自己邏輯override接口的validate方法。
這樣,對于一個(gè)servlet,想讓它上什么樣的鎖,只要讓它拿到對應(yīng)的子類的引用即可,如下圖中的ClientServlet,我們規(guī)定只能有“學(xué)生或教師”才能訪問它。它的部分代碼便是:

1
//new對應(yīng)的Validator接口的子類。
2
//這里是學(xué)生或教師可訪問,因此要new Student_Or_Teacher_Validator
3
Validator validator = new Student_Validator();
4
//然后調(diào)用驗(yàn)證方法就可以了
5
boolean ok = validator.validate();
至此,第一個(gè)解決方案就出來了。
思考:
不足 |
validator接口的子類數(shù)目隨“角色數(shù)”成“指數(shù)級”增長,數(shù)量太多;而且子類中重復(fù)邏輯的代碼很多,如“Student_Or_Teacher_Validator”就重復(fù)了“Student_Validator”和“Teacher_Validator”的邏輯,萬一“Student_Validator”的邏輯要改,只要涉及Student的子類都要跟著改,維護(hù)上不方便。 |
進(jìn)一步改進(jìn)的可能 |
“Student_Or_Teacher_Validator類”的validate方法很大程度上就是“Student_Validator類”的validate方法和“Teacher_Validator類”的validate方法“或操作”出來的結(jié)果。
因此,能不能考慮由“Student_Validator類的validate方法”和“Teacher_Validator的validate方法”動(dòng)態(tài)的構(gòu)造一個(gè)功能如“Student_Or_Teacher_Validator類的validate方法”。
這樣,“Student_Or_Teacher_Validator”就可以省略了,只剩下一些原子的“角色類”。要實(shí)現(xiàn)Student_Or_Teacher_Validator的驗(yàn)證功能,拿Student_Validator和Teacher_Validator裝配一下就可以了。 |
結(jié)論,需要根據(jù)實(shí)際情況,動(dòng)態(tài)的改變(裝配)“Validator接口對象”的validate方法。
第一個(gè)火花就是“裝飾模式”。它可以讓客戶端動(dòng)態(tài)的組裝對象的方法。真神奇!
第二版來啦

注:上圖中出現(xiàn)了AndRelation和And的一系列角色類,可以暫時(shí)省略不看,因?yàn)榍懊嬲f了,現(xiàn)在還沒有“與”關(guān)系這個(gè)需求。只看Or的就可以。
我喜歡叫這個(gè)模式為洋蔥模式,一層包一層,最外層對象某方法的邏輯是由內(nèi)部一層一層對象的同一方法組合出來的。
使用了這個(gè)模式,便不用如一版那樣實(shí)現(xiàn)那么多子類,只要實(shí)現(xiàn)幾個(gè)“角色類”即可,這里有3個(gè)(學(xué)生角色:Or_Student、教師角色:Or_Teacher、游客角色:Or_Guest)。所有一版中別的子類都可以由這3個(gè)組裝出來。
如要生成一版中的Student_Or_Teacher_Validator對象,可以用Or_Student和Or_Teacher兩個(gè)對象“Or”出來:
1
Validator validator = new Or_Student(new Or_Teacher(OrRelation(req)));
如要生成一版中的Guest_Or_Student_Or_Teacher_Validator對象,可以用Or_Student、Or_Teacher和Or _Guest三個(gè)對象“Or”出來:
1
Validator validator = new Or_Student(new Or_Teacher(new Or_Guest(OrRelation(req))));
這種一層包一層的new方式,是不是很像洋蔥?第一次看是很不習(xí)慣,看多了就覺得習(xí)慣了。
對客戶端的影響:
一版中,客戶端要什么樣的驗(yàn)證類,就直接使用具體類。
二版中,客戶端要什么樣的驗(yàn)證類,它的工作多了那么一丁點(diǎn),它需要先組裝一下,正如上面的例子。這種組裝的方法很易于理解和使用,不會(huì)給客戶端帶來任何的不便。如果實(shí)在覺得客戶端組裝不出來(傻B客戶端),也可以搞個(gè)工廠給它supply!
優(yōu)點(diǎn):
相對一版最明顯的優(yōu)點(diǎn)就是類的數(shù)目少了很多。
一版不是說“指數(shù)級”嗎?這里只是線性的了。假設(shè)某一天系統(tǒng)拓展到有10個(gè)角色,一版就有2的10次方那么多個(gè),也就是1024個(gè)驗(yàn)證類。
而二版還是10個(gè)角色類,別的都可以在客戶端使用的時(shí)候,動(dòng)態(tài)的組裝
更重要的是代碼結(jié)構(gòu)好了,重復(fù)邏輯少了。每個(gè)邏輯都以最atomic的大小放到最應(yīng)該的地方。
進(jìn)而,維護(hù)的代價(jià)少多了。如某天“教師角色”的驗(yàn)證邏輯發(fā)生了變化,只要改動(dòng)Or_Teacher一個(gè)地方即可。
MARCO ZHANG 2006年2月18日23:49:56