10-JAVA設(shè)計(jì)模式第十課:數(shù)據(jù)校驗(yàn)器架構(gòu)模式組_第1頁(yè)
10-JAVA設(shè)計(jì)模式第十課:數(shù)據(jù)校驗(yàn)器架構(gòu)模式組_第2頁(yè)
10-JAVA設(shè)計(jì)模式第十課:數(shù)據(jù)校驗(yàn)器架構(gòu)模式組_第3頁(yè)
10-JAVA設(shè)計(jì)模式第十課:數(shù)據(jù)校驗(yàn)器架構(gòu)模式組_第4頁(yè)
10-JAVA設(shè)計(jì)模式第十課:數(shù)據(jù)校驗(yàn)器架構(gòu)模式組_第5頁(yè)
已閱讀5頁(yè),還剩11頁(yè)未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說(shuō)明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡(jiǎn)介

1、本文闡述軟件架構(gòu)與設(shè)計(jì)模式,它為架構(gòu)師和開發(fā)人員提供了一組關(guān)于數(shù)據(jù)校驗(yàn) 的架構(gòu)模式(隔離校驗(yàn)器,可組裝校驗(yàn)器,動(dòng)態(tài)策略校驗(yàn)器,動(dòng)態(tài)注冊(cè)校驗(yàn)器等), 數(shù)據(jù)校驗(yàn)是任何類型的開發(fā)中都不可或缺的環(huán)節(jié),如果沒(méi)有統(tǒng)一的架構(gòu),可能校 驗(yàn)代碼會(huì)遍布整個(gè)應(yīng)用,如何將數(shù)據(jù)校驗(yàn)與應(yīng)用邏輯解耦,如何適應(yīng)各種粒度的 數(shù)據(jù)和各種復(fù)雜程度業(yè)務(wù)規(guī)則,正是本文要探討的。在我們各種類型的應(yīng)用開發(fā)中有一個(gè)必不可少的環(huán)節(jié)數(shù)據(jù)校驗(yàn),無(wú)論是大型企 業(yè)應(yīng)用,還是一個(gè)簡(jiǎn)單的程序。如果沒(méi)有統(tǒng)一的架構(gòu),可能校驗(yàn)代碼會(huì)遍布整個(gè) 應(yīng)用,一旦校驗(yàn)規(guī)則改變就需要修改多處代碼,這是一種不好的設(shè)計(jì),因?yàn)閿?shù)據(jù) 校驗(yàn)與應(yīng)用邏輯耦合得太緊。數(shù)據(jù)校驗(yàn)不外乎語(yǔ)法校驗(yàn)

2、和語(yǔ)義校驗(yàn)兩類,本文描 述了一組架構(gòu)上的模式來(lái)對(duì)這兩類需求提供解決方案。該模式組按照待校驗(yàn)數(shù)據(jù) 的粒度大小和業(yè)務(wù)規(guī)則的復(fù)雜程度分成多種類型:隔離校驗(yàn)器,可組裝校驗(yàn)器, 動(dòng)態(tài)策略校驗(yàn)器,動(dòng)態(tài)注冊(cè)校驗(yàn)器等。大家可以針對(duì)自己的應(yīng)用選擇合適的架構(gòu)。 應(yīng)用這組模式還可以獲得一個(gè)好處,如果需要的話,我們可以把數(shù)據(jù)校驗(yàn)器當(dāng)作 一個(gè)橫切關(guān)注點(diǎn)(Crosscut concern),應(yīng)用 AOP (Aspect of Programming) 技術(shù),這樣可以徹底分離出數(shù)據(jù)校驗(yàn)邏輯代碼。問(wèn)題引出讓我們從幾個(gè)應(yīng)用場(chǎng)景(user scenario)開始吧,第一個(gè)場(chǎng)景是網(wǎng)站上的注冊(cè) 用戶,注冊(cè)時(shí)需要填寫很多數(shù)據(jù),這些數(shù)據(jù)

3、都需要校驗(yàn)后才能寫進(jìn)數(shù)據(jù)庫(kù),比如 用戶名,校驗(yàn)規(guī)則可能是:用戶名由az的英文字母(不區(qū)分大小寫)、09的 數(shù)字、點(diǎn)、減號(hào)或下劃線組成,長(zhǎng)度為318個(gè)字符。這種關(guān)于數(shù)據(jù)的結(jié)構(gòu)正 確性方面的校驗(yàn)我們稱之為語(yǔ)法校驗(yàn)。而身份證號(hào)碼這種數(shù)據(jù),它需要根據(jù)出生 日期校驗(yàn)身份證號(hào)碼的正確性,不僅僅是填夠了 16 或 19 位數(shù)字就行。這種關(guān) 于數(shù)據(jù)的內(nèi)容正確性方面的校驗(yàn)稱之為語(yǔ)義校驗(yàn)。一般情況下語(yǔ)法和語(yǔ)義方面的 校驗(yàn)是在一塊處理的,比如身份證號(hào)碼,必然也需要校驗(yàn)數(shù)據(jù)是否全是數(shù)字和必 須是 16 或 19 位,這是語(yǔ)法校驗(yàn),同時(shí)它需要和出生日期相符,這又是語(yǔ)義校 驗(yàn)。從架構(gòu)的角度而言,這種情況下區(qū)分語(yǔ)法和語(yǔ)義的

4、意義不太大,因?yàn)闆](méi)必要 把它分成兩個(gè)步驟用兩個(gè)方法來(lái)處理。但是有些應(yīng)用,比如數(shù)據(jù)是一段 XML 的 文本串,首先需要校驗(yàn)XML字符串的結(jié)構(gòu)一語(yǔ)法是否符合相應(yīng)的schema,然后 再校驗(yàn)其中某個(gè)元素的內(nèi)容語(yǔ)義的正確性,這可能就需要分開來(lái)處理比較合適 因?yàn)檎Z(yǔ)法校驗(yàn)是業(yè)務(wù)無(wú)關(guān)的,而后者的語(yǔ)義校驗(yàn)是業(yè)務(wù)相關(guān)的,業(yè)務(wù)相關(guān)就意味 著一旦業(yè)務(wù)規(guī)則改變,校驗(yàn)規(guī)則就可能改變,所以這種情況最好將語(yǔ)義校驗(yàn)分離 出來(lái)。第二個(gè)應(yīng)用場(chǎng)景是一個(gè)MDA (Model Driven Architecture)工具開發(fā)的例子, 我們都用過(guò)大名鼎鼎的Rational Rose或Microsoft Visio。這些工具都提供 從UM

5、L模型生成代碼的功能,這就是MDA,它們將UML模型映射成模型的元數(shù) 據(jù)(meta-data)(稱之為元模型meta-model),然后從元模型可以轉(zhuǎn)換成各種支 持語(yǔ)言的代碼,如Java, C+。當(dāng)我們?cè)谝晥D上畫一個(gè)UML元素(如類Class), 然后為其定義了某種 Stereotype 來(lái)標(biāo)識(shí)他的業(yè)務(wù)語(yǔ)義,比如數(shù)據(jù)庫(kù)的表 Table 或者自定義的一個(gè)用于表示 WebService 的 Service 元素。接下來(lái)我們要將該 元素生成相應(yīng)的代碼,這時(shí)當(dāng)你選定元素時(shí),運(yùn)行時(shí)系統(tǒng)并不知道該元素是普通 的Class,還是Table,因?yàn)樵谶\(yùn)行時(shí)環(huán)境中都是UML的Class實(shí)例對(duì)象,這 就需要我們提供

6、校驗(yàn)邏輯來(lái)處理了,處理 Class 的校驗(yàn)邏輯和 Table 的校驗(yàn)邏 輯自然不應(yīng)該放在一起,更何況如果是自定義的擴(kuò)展元素,根本不可能把校驗(yàn)代 碼寫到已有系統(tǒng)里去。這就需要我們提供一個(gè)統(tǒng)一的校驗(yàn)器接口,不同的校驗(yàn)邏 輯封裝在單獨(dú)的類中。進(jìn)一步,我們需要對(duì)這些獨(dú)立的校驗(yàn)器進(jìn)行集中組裝和管 理,因?yàn)槲覀儾槐孛看味既?shí)例化這些工具類,實(shí)例化后將它們緩存起來(lái)就可以 了。第三個(gè)應(yīng)用場(chǎng)景是一個(gè)銀行并購(gòu)的案例,假如銀行A并購(gòu)了銀行B,兩家銀行 都有各自已有的電子銀行應(yīng)用,并購(gòu)后要將兩家應(yīng)用整合成一個(gè)統(tǒng)一的應(yīng)用,其 中有一個(gè)余額查詢業(yè)務(wù),在進(jìn)行具體的查詢操作事務(wù)之前,需要校驗(yàn)用戶輸入的 帳號(hào)account,兩

7、家銀行已有的帳號(hào)各有不同的創(chuàng)建規(guī)則,比如銀行A是16位 數(shù)字作為帳戶,首 4 位是銀行代號(hào),第二個(gè) 4 位是地區(qū)代號(hào),第三個(gè) 4 位是 網(wǎng)點(diǎn)代號(hào),尾4位是用戶編號(hào)。而銀行B則是19位數(shù)字作為帳戶,各個(gè)區(qū)段 的含義也和銀行 A 不一樣,這就要求用戶填寫一個(gè)帳戶的時(shí)候,后臺(tái)必須對(duì)應(yīng) 兩套數(shù)據(jù)校驗(yàn)規(guī)則,而且應(yīng)用需要根據(jù)一定的規(guī)則來(lái)選擇銀行 A 的校驗(yàn)策略或 銀行 B 的校驗(yàn)策略。而且更復(fù)雜的情況是,銀行的帳戶還可能是升位后的(比 如從 12 位升到 16 位),這樣必須同時(shí)兼顧新舊帳戶,也就是說(shuō)有多套校驗(yàn)規(guī) 則來(lái)處理,我們的數(shù)據(jù)校驗(yàn)器需要支持業(yè)務(wù)規(guī)則的動(dòng)態(tài)切換。這里面可能有一個(gè) 有爭(zhēng)議的地方,校驗(yàn)帳

8、號(hào)時(shí)需要有具體的業(yè)務(wù)規(guī)則支持,那么這算不算是業(yè)務(wù)邏 輯呢,當(dāng)然這個(gè)校驗(yàn)邏輯并不那么純粹,軟件設(shè)計(jì)并不是個(gè)黑白的二元世界,各 種層次的對(duì)象混合在一起很正常,我們也不大可能什么東西都能做個(gè)分水嶺把 它們隔離開來(lái)。另外,這里的校驗(yàn)邏輯還是和銀行應(yīng)用別的業(yè)務(wù)邏輯不大一樣, 比如轉(zhuǎn)帳交易,這個(gè)動(dòng)作的觸發(fā)是一定要在一個(gè)高安全可靠的事務(wù)中執(zhí)行的,而 我們的校驗(yàn)帳號(hào)過(guò)程可能不需要運(yùn)行在事務(wù)中,或者只運(yùn)行在低安全可靠級(jí)別的 事務(wù)中即可。這是有本質(zhì)區(qū)別的,所以把這種摻有業(yè)務(wù)規(guī)則的校驗(yàn)劃分到校驗(yàn)邏 輯里而不是業(yè)務(wù)邏輯中是有理由的。隔離校驗(yàn)器針對(duì)上述第一類應(yīng)用場(chǎng)景,我們只需要把數(shù)據(jù)校驗(yàn)邏輯從其他業(yè)務(wù)邏輯中剝離出 來(lái),

9、將校驗(yàn)邏輯委任到一個(gè)單獨(dú)的校驗(yàn)類中去。把校驗(yàn)職責(zé)分離出來(lái)后,第一個(gè) 好處是:一旦我們需要更改校驗(yàn)邏輯,只要修改校驗(yàn)類代碼即可,而不用修改其 他任何業(yè)務(wù)邏輯類。第二個(gè)好處是:可以集中管理控制所有的數(shù)據(jù)校驗(yàn)邏輯,提 高了代碼的內(nèi)聚性,而且讓代碼簡(jiǎn)潔、清晰。當(dāng)然這里說(shuō)的所有數(shù)據(jù)集中控制不 一定就是全放在一個(gè)類中,如果有必要,也可以將數(shù)據(jù)按照不同的類型分組,每 一個(gè)組封裝在一個(gè)校驗(yàn)類中。第三個(gè)好處是可重用性高,校驗(yàn)邏輯封裝成了一個(gè) 工具類,自然可重用性大大提高。在設(shè)計(jì)這個(gè)隔離校驗(yàn)器類時(shí)還有一些需要權(quán)衡的地方,在設(shè)計(jì)某一個(gè)數(shù)據(jù)的校驗(yàn) 方法時(shí),比如用戶名的校驗(yàn),如果數(shù)據(jù)出錯(cuò)了,簡(jiǎn)單的情況下,我們只需返回一

10、 個(gè) boolean 值,告訴用戶數(shù)據(jù)有誤。而如果是身份證號(hào)碼這類數(shù)據(jù)出錯(cuò)了,可 能就需要提供更細(xì)粒度的錯(cuò)誤類型給用戶,告訴用戶是與出生日期不符還是位數(shù) 不夠。對(duì)這種錯(cuò)誤種類較多的情況,我們可以返回錯(cuò)誤代號(hào)(如 int 值)來(lái)區(qū) 別各種錯(cuò)誤,這是非面向?qū)ο笳Z(yǔ)言的一種做法,在面向?qū)ο笾形覀兛梢杂靡粋€(gè)異 常 Exception 來(lái)返回錯(cuò)誤類型,這比返回錯(cuò)誤代號(hào)更好,因?yàn)殄e(cuò)誤代號(hào)需要解 析成具體的錯(cuò)誤信息,這個(gè)解析工作還得由校驗(yàn)器類的 API 使用者來(lái)調(diào),這個(gè)使 用者是其它的業(yè)務(wù)邏輯類,這就是說(shuō)業(yè)務(wù)邏輯類還是耦合了數(shù)據(jù)校驗(yàn)錯(cuò)誤處理邏 輯,顯然不如用異常處理來(lái)的徹底。代碼如下:清單 1: UserIn

11、foValidator.javapublic abstract class UserInfoValidator public static boolean validateUserID(String uid) boolean isValid = false;/ 校驗(yàn)規(guī)則return isValid;public static boolean validteEmail(String email) boolean isValid = false;/ 校驗(yàn)規(guī)則return isValid;public static void validateSSN(SSNDataObject ssn)throws

12、DataValidationException if (ssn = null)throw new DataValidationException(No data found.);String idCard = ssn.getIdCard();if (idCard = null) | (idCard.equals()throw new DataValidationException(No id.card data found.);if (!(idCard.length() = 15) | (idCard.length() = 18)throw new DataValidationExceptio

13、n(ID.card length must be 15 or 18.);Date birthDay = ssn.getBirthDay();if (birthDay = null)throw new DataValidationException(No birthday data found.);int sex = ssn.getSex();if (sex = 0)throw new DataValidationException(No sex data found.);/ 生日校驗(yàn)規(guī)則/ if (.)/ throw new DataValidationException(ID.card di

14、dnt match birthday.);int idSex = Integer.parseInt(idCard.substring(idCard.length() - 1);if (idSex % sex != 0)throw new DataValidationException(ID.card didnt match sex.);從上面代碼可以看出,我們用了靜態(tài) static 方法,因?yàn)槲覀冞@是個(gè)工具類,沒(méi) 有什么狀態(tài)需要存儲(chǔ),所以不需要實(shí)例化類。而且調(diào)用校驗(yàn)方法會(huì)很頻繁,用靜 態(tài)方法可以提升性能。另外還有一點(diǎn)值得一提,我們封裝了一個(gè)身份證數(shù)據(jù)類,里面包含了三個(gè)屬性: 身份證號(hào),出生日期

15、,性別。驗(yàn)證身份證號(hào)需要出生日期和性別奇偶碼這一點(diǎn)是 沒(méi)有異議的,但為什么不用三個(gè)單獨(dú)的參數(shù)呢,這里的封裝為以后提供了更大的 靈活性,比如將來(lái)我們打算將身份證驗(yàn)證邏輯做得更精細(xì),需要判斷出生地區(qū)的 代碼是否和身份證的頭幾位一致,這可能就需要四個(gè)參數(shù)了,或者我們的出生日 期需要換一個(gè)類(Date-Calendar)來(lái)表示,顯然我們只需要修改身份證數(shù)據(jù)封 裝類,而不用修改調(diào)用接口。可組裝校驗(yàn)器針對(duì)第二類場(chǎng)景,我們對(duì)每一個(gè)數(shù)據(jù)類提供一個(gè)獨(dú)立的校驗(yàn)規(guī)則類,因?yàn)檫@個(gè)數(shù) 據(jù)類本身已經(jīng)包含了語(yǔ)法和語(yǔ)義邏輯。語(yǔ)法邏輯是與數(shù)據(jù)結(jié)構(gòu)相關(guān)的,在我們的 示例中,是判斷對(duì)象是否是UML的Class實(shí)例。而語(yǔ)義邏輯是與業(yè)

16、務(wù)規(guī)則相關(guān)的, 每一個(gè)數(shù)據(jù)類關(guān)聯(lián)的業(yè)務(wù)規(guī)則不盡相同,可能來(lái)自不同領(lǐng)域,或不同的業(yè)務(wù)組件 或系統(tǒng);另外由于業(yè)務(wù)規(guī)則的易變性較強(qiáng),可擴(kuò)展性和可配置性要求也較高,所 以有必要為每一個(gè)數(shù)據(jù)類設(shè)置專屬的校驗(yàn)類。這里我們將每一個(gè)校驗(yàn)類稱作一條 校驗(yàn)規(guī)則(Rule)。校驗(yàn)規(guī)則類的接口和實(shí)現(xiàn)代碼如下:清單 2: IVRule.javapublic interface IVRule /*validate value by domain rule.*/public boolean isValid(Object value);/*validate value by domain rule.*/public void

17、 validate(Object value) throws DataValidationException; 清單 3: ServiceVRule.java public class ServiceVRule implements IVRule private static String STEREOTYPE_NAME = Service;public void validate(Object value) throws DataValidationException if (value instanceof Class) Stereotype st = (Class) value).get

18、Stereotype();String name = st.getName();if (STEREOTYPE_NAME.equals(name) String wsdl = (String) st.getProperty(WSDL);if (wsdl = null) | (wsdl.equals()throw new DataValidationException(No WSDL file is defined.); elsethrow new DataValidationException(It is not a Service Object.); elsethrow new DataVal

19、idationException(It is not a UML Class model.); 在這里,我們還是提供了兩種校驗(yàn)結(jié)果返回機(jī)制,boolean值和拋出異常,在具 體應(yīng)用時(shí)大家可以選擇一個(gè)即可。這里的校驗(yàn)規(guī)則實(shí)現(xiàn)是關(guān)于 WebService 的, 它的語(yǔ)法校驗(yàn)是檢查數(shù)據(jù)是否是 UML 的 Class 對(duì)象,而語(yǔ)義校驗(yàn)是檢查 Stereotype 是否是 Service,并檢查 Stereotype 中是否含有 WSDL(Web service description language) 屬性。接下來(lái),怎么應(yīng)用這些校驗(yàn)規(guī)則 rule 呢?一個(gè)系統(tǒng)中會(huì)有很多校驗(yàn)規(guī)則類,那 么就需要一個(gè)管

20、理機(jī)制來(lái)管理這些校驗(yàn)類,最簡(jiǎn)單的我們只需定義一個(gè)方法 validate(Object value, IVRule rule),提供一層簡(jiǎn)單的封裝,它可以起到一 個(gè)代理的作用,比如,應(yīng)用 Proxy 模式,我們可以對(duì)校驗(yàn)規(guī)則本身做一些安全 性認(rèn)證方面的工作,然后才決定是否可以用該校驗(yàn)規(guī)則。而更完善的管理機(jī)制是 提供一個(gè)更靈活的環(huán)境,讓用戶可以動(dòng)態(tài)組裝,改變,查找校驗(yàn)規(guī)則類?;舅?路是:我們將校驗(yàn)規(guī)則類當(dāng)作一種可重用資源,提供一個(gè)組裝工廠環(huán)境,用戶可 以將校驗(yàn)規(guī)則 rule 注冊(cè)到工廠里,工廠會(huì)實(shí)例化和緩存這些類,并提供查找服 務(wù);然后提供一個(gè)校驗(yàn)器來(lái)從工廠里查找出相應(yīng)的校驗(yàn)規(guī)則 rule 類,

21、為用戶提 供校驗(yàn)服務(wù)。這里面用到了 Factory, Flyweight, Registry 模式。(本文引用 的模式請(qǐng)參考相關(guān)模式)第一步,我們?cè)O(shè)計(jì)一個(gè)組裝工廠類,它提供實(shí)例化、緩存、和查找服務(wù),很顯然, 實(shí)例化類是一個(gè) Factory 模式的基本職責(zé),將實(shí)例緩存 cache 是一個(gè) Flyweight 模式的基本職責(zé),查找服務(wù)可以很簡(jiǎn)單,在我們的例子中就是從一個(gè) Map 中取出校驗(yàn)規(guī)則 rule 實(shí)例,也可以復(fù)雜化,比如我們的校驗(yàn)規(guī)則類是一個(gè) 遠(yuǎn)程資源,或者是實(shí)例化這個(gè)類需要用到其它的遠(yuǎn)程資源,如數(shù)據(jù)庫(kù),那這個(gè)查 找功能實(shí)現(xiàn)起來(lái)可能就復(fù)雜些,可以通過(guò) JNDI 來(lái)查找,也可以將遠(yuǎn)程資源暴露

22、 成服務(wù)(Web Service )并注冊(cè)到 UDDI (Universal Discover Descrip tion and Integration),然后從UDDI中查找服務(wù)。從這里我們可以看到這個(gè)組裝工廠類 具備管理校驗(yàn)規(guī)則 rule 類的整個(gè)生命周期的職責(zé),這為校驗(yàn)器應(yīng)用提供了很大 的靈活性和可擴(kuò)展性,假如我們今后需要實(shí)現(xiàn)一個(gè)實(shí)例池或資源池 Pool 來(lái)管理 這些校驗(yàn)規(guī)則實(shí)例,那么只需要將組裝工廠類的功能稍作修改和擴(kuò)展就可,而不 必觸及校驗(yàn)器應(yīng)用的其他類,因?yàn)槲覀円呀?jīng)將實(shí)例的管理邏輯從整個(gè)校驗(yàn)器應(yīng)用 中剝離出來(lái),管理職責(zé)的變化只局限在組裝工廠類內(nèi),對(duì)別的類是封閉的,這正 體現(xiàn)面向?qū)ο?/p>

23、的基本設(shè)計(jì)原則之一 Open-Close 原則(對(duì)修改封閉,對(duì)擴(kuò)展開放)。 組裝工廠類的代碼如下:清單 4: VRuleAssemblerFactory.javapublic class VRuleAssemblerFactory private static Map rules = new HashMap();/* 查找校驗(yàn)規(guī)則 Rule 。*/public static IVRule lookupVRule(String ruleHandler) if (ruleHandler = null)return null;IVRule rule = null;if (rules.contains

24、Key(ruleHandler)rule = (IVRule) rules.get(ruleHandler);return rule;/* 注冊(cè) /加入一個(gè)校驗(yàn)規(guī)則 Rule.*/public static void addVRule(String ruleHandler, Class ruleClass) if (ruleHandler != null) & (ruleClass != null) try rules.put(ruleHandler, ruleClass.newInstance(); catch (InstantiationException e) e.printStackT

25、race(); catch (IllegalAccessException e) e.printStackTrace();/* 批量載入校驗(yàn)規(guī)則 Rule, 一般在應(yīng)用系統(tǒng)初始化時(shí)調(diào)用。*/public static void assembleVRules() addVRule(Table, TableVRule.class);addVRule(Service, ServiceVRule.class);從上面的代碼可以看到,我們用一個(gè) Map 作為緩存庫(kù),注冊(cè)一個(gè)校驗(yàn)規(guī)則時(shí)以 字符串作為關(guān)鍵字,當(dāng)然也可以用別的自定義類型,在查詢時(shí)利用了 Map 的查 找功能很簡(jiǎn)單高效地實(shí)現(xiàn)了查找功能,另外我們

26、還定義了一個(gè)批量載入校驗(yàn)規(guī)則 的方法,這個(gè)功能是為了用戶使用方便,在應(yīng)用系統(tǒng)初始化時(shí)執(zhí)行一次,而且可 以透明地載入校驗(yàn)規(guī)則,在本例中只是硬編碼了這些校驗(yàn)規(guī)則類,需要的話我們 可以從別的元數(shù)據(jù)文件中(XML或CSV文件)導(dǎo)入。還需注意一點(diǎn)的是,這個(gè)組裝工廠是一個(gè)全局類,在這里是以靜態(tài) static 方式 實(shí)現(xiàn)的,當(dāng)然也可以以 Singleton 方式來(lái)實(shí)現(xiàn),還可以以線程安全 ThreadLocal 的方式來(lái)實(shí)現(xiàn)。第二步,我們?cè)O(shè)計(jì)校驗(yàn)器類,校驗(yàn)器類應(yīng)該作為整個(gè)校驗(yàn)器應(yīng)用的Fagade,用 戶需要校驗(yàn)數(shù)據(jù)時(shí)只需和它打交道,這就很好的把校驗(yàn)規(guī)則類隱藏了起來(lái),因此 校驗(yàn)器的職責(zé)有查找相應(yīng)的校驗(yàn)規(guī)則類和執(zhí)

27、行校驗(yàn)。校驗(yàn)器類的接口和實(shí)現(xiàn)代碼 如下:清單 5: IValidator.javapublic interface IValidator /*通過(guò)關(guān)鍵字 ruleHandler 查詢校驗(yàn)規(guī)則來(lái)校驗(yàn)數(shù)據(jù)。*/public boolean isValid(Object value, String ruleHandler);/*通過(guò)關(guān)鍵字 ruleHandler 查詢校驗(yàn)規(guī)則來(lái)校驗(yàn)數(shù)據(jù)。返回異常。*/public void validate(Object value, String ruleHandler) throws DataValidationException;/*直接指定校驗(yàn)規(guī)則類來(lái)校驗(yàn)數(shù)

28、據(jù)。*/public boolean isValid(Object value, IVRule rule);/*直接指定校驗(yàn)規(guī)則類來(lái)校驗(yàn)數(shù)據(jù),返回異常。*/public void validate(Object value, IVRule rule) throws Exception; 清單 6: AssemblyValidator.java public class AssemblyValidator implements IValidator private static AssemblyValidator instance = null;private AssemblyValidator

29、() public static synchronized AssemblyValidator getInstance() if (instance = null)instance = new AssemblyValidator();return instance;public boolean isValid(Object value, String ruleHandler) boolean valid = false;IVRule rule = VRuleAssemblerFactory.lookupVRule(ruleHandler);if (rule != null)valid = ru

30、le.isValid(value);return valid;public void validate(Object value, String ruleHandler)throws DataValidationException IVRule rule = VRuleAssemblerFactory.lookupVRule(ruleHandler);if (rule != null)rule.validate(value);public boolean isValid(Object value, IVRule rule) boolean valid = false;if (rule != n

31、ull)valid = rule.isValid(value);return valid;public void validate(Object value, IVRule rule) throws Exception if (rule != null)rule.validate(value);從上面代碼可以看到,我們使用了 Single ton模式,因?yàn)闆](méi)必要每次都實(shí)例化校 驗(yàn)器類。另外我們?cè)谶@里提供了兩套返回機(jī)制,還有兩套取校驗(yàn)規(guī)則的方式,這 都可以根據(jù)實(shí)際應(yīng)用作出取舍??山M裝校驗(yàn)器的架構(gòu)圖如下: 圖 1:可組裝校驗(yàn)器的架構(gòu)圖FLFW9 wb:labr();” A嚴(yán) 9 leid () v

32、otedEit (u I L.lf :陽(yáng) 11叵!fKpYjjyg 丄止磁嘰1峻丄.】 剛?竊f】nw論1計(jì)池1 () 心冊(cè)( validate ( j# SataVaikia tk?ni&(c ept kiri i G必御罔WjFrlXaie :牛 勺.半過(guò)昨Id浪I警t今.AMennUyVdklartHi ( 1*護(hù)t】門氏,ipp :色 kV.-iil (e vdi late (j網(wǎng)如 3 vikiatB ( KVsIri (、 vafclata ( Y動(dòng)態(tài)策略校驗(yàn)器針對(duì)第三類場(chǎng)景,我們必須支持一種數(shù)據(jù)對(duì)應(yīng)有多套業(yè)務(wù)校驗(yàn)規(guī)則,我們可以把 每種業(yè)務(wù)規(guī)則都建模成一個(gè)校驗(yàn)規(guī)則類,但這樣做靈活性

33、和可擴(kuò)展性就很差了。 如果我們對(duì)一種數(shù)據(jù)只用一個(gè)校驗(yàn)規(guī)則類,而將多套業(yè)務(wù)規(guī)則建模成多種策略, 在校驗(yàn)規(guī)則類中應(yīng)用這些策略,這樣做好處在于:一可以對(duì)用戶隱藏業(yè)務(wù)規(guī)則, 二是將來(lái)對(duì)策略進(jìn)行修改或增加新的策略都不需要更改用戶的調(diào)用接口,三是我 們可以在運(yùn)行時(shí)動(dòng)態(tài)地改變業(yè)務(wù)規(guī)則策略。要實(shí)現(xiàn)上述需求,我們只需要在可 組裝校驗(yàn)器架構(gòu)的基礎(chǔ)上,對(duì)校驗(yàn)規(guī)則 rule 類引入 Strategy 模式。首先我們 設(shè)計(jì)策略類,對(duì)于銀行并購(gòu)這個(gè)例子,對(duì)帳號(hào)的校驗(yàn)可以分成以下幾步:校驗(yàn)銀 行代號(hào),地區(qū)代號(hào),網(wǎng)點(diǎn)代號(hào),用戶代號(hào),因此在這里我們先根據(jù)各個(gè)銀行的業(yè) 務(wù)策略對(duì)帳號(hào)進(jìn)行分割,比如 19 位的帳號(hào)分成 4 位銀行代

34、號(hào),4 位地區(qū)代號(hào), 4 位網(wǎng)點(diǎn)代號(hào),和 7 位用戶代號(hào),然后再執(zhí)行上述幾步校驗(yàn)。業(yè)務(wù)策略類的接 口和實(shí)現(xiàn)代碼如下:清單 7:IAccountStrategy.javapublic interface IAccountStrategy /* 獲得字符串分割規(guī)則,比如 19 位的帳號(hào)分成 4,4,4,7 。*/public int getSeperatedNumbers();/* 校驗(yàn)銀行代號(hào)。*/public boolean validateBankCode(String bankCode);/* 校驗(yàn)地區(qū)代號(hào)。*/public boolean validateDistrictCode(Str

35、ing districtCode);/*校驗(yàn)網(wǎng)點(diǎn)代號(hào)。 */ public boolean validateSiteCode(String siteCode);/*校驗(yàn)用戶代號(hào)。 */ public boolean validateUserCode(String userCode);清單 8:BankAAccountStrategy.javapublic class BankAAccountStrategy implements IAccountStrategy private static int seperatedNumbers = 4, 4, 4, 4 ;public int getSe

36、peratedNumbers() return seperatedNumbers;public boolean validateBankCode(String bankCode) / query bank id from local database or meta-data file.String bankID = 9880;if (bankID.equals(bankCode)return true;return false;public boolean validateDistrictCode(String districtCode) if (districtCode != null)&

37、 (districtCode.length() = seperatedNumbers1)if (districtCode.startsWith(8)return true;return false;public boolean validateSiteCode(String siteCode) if (siteCode != null) & (siteCode.length() = seperatedNumbers2) if (siteCode.startsWith(1)return true;return false;public boolean validateUserCode(Strin

38、g userCode) if (userCode != null) & (userCode.length() = seperatedNumbers3) return true;return false;接下來(lái)我們?cè)O(shè)計(jì)校驗(yàn)規(guī)則類,該類主要有選擇策略和使用策略來(lái)校驗(yàn)兩種職責(zé) 代碼如下:清單 9:public class AccountVRule implements IVRule private static Map strategies = new HashMap();static / 注冊(cè)策略類strategies.put(new Integer(16), new BankAAccountSt

39、rategy(); strategies.put(new Integer(19), new BankBAccountStrategy();public void validate(Object value) throws DataValidationException if (value = null)throw new DataValidationException(Account cant be empty.);if (!(value instanceof String)throw new DataValidationException(Cant cast Object to String

40、.); String val = (String) value;if (strategies.containsKey(new Integer(val.length() IAccountStrategy strat = (IAccountStrategy) strategies.get(new Integer(val.length();int sepNum = strat.getSeperatedNumbers();/validate bank codeString bankCode = val.substring(0, sepNum0);if (!strat.validateBankCode(

41、bankCode)throw new DataValidationException(Bank code + bankCode+ doesnt match account rule);/validate district code.String disCode = val.substring(sepNum0, sepNum0 + sepNum1);if (!strat.validateDistrictCode(disCode)throw new DataValidationException(District code + disCode+ doesnt match account rule)

42、;/validate site codeString siteCode = val.substring(sepNum0 + sepNum1, sepNum0+ sepNum1 + sepNum2);if (!strat.validateSiteCode(siteCode)throw new DataValidationException(Site code + siteCode+ doesnt match account rule);/validate user codeString userCode = val.substring(val.length() - sepNum3);if (!s

43、trat.validateUserCode(userCode)throw new DataValidationException(User code + userCode+ doesnt match account rule); elsethrow new DataValidationException(The length of input account NO + val.length() + doesnt match account rule.);在這里我們?cè)僖淮斡玫搅?Registry 模式,可以看到這個(gè)校驗(yàn)規(guī)則類具有管理和 緩存策略類的職責(zé)。當(dāng)然我們還可以在該類中增加一個(gè)方法 reg

44、iesterStrategy() 用來(lái)在運(yùn)行時(shí)動(dòng)態(tài)地增加業(yè)務(wù)規(guī)則策略,但目前我們的應(yīng) 用沒(méi)有這么復(fù)雜的需求,就算將來(lái)有也很容易重構(gòu)目前的架構(gòu),所以這個(gè)設(shè)計(jì)活 動(dòng)應(yīng)該點(diǎn)到為止。這也是設(shè)計(jì)中的一個(gè)權(quán)衡點(diǎn),設(shè)計(jì)是沒(méi)有絕對(duì)完美的,人們?cè)?追逐絕對(duì)完美設(shè)計(jì)的過(guò)程中經(jīng)常把對(duì)未來(lái)的種種揣測(cè)當(dāng)作真正的需求,結(jié)果只 能是危及整個(gè)設(shè)計(jì),導(dǎo)致代碼臃腫,難以維護(hù),僵化,靈活性差。適度的設(shè)計(jì) 才是完美的。動(dòng)態(tài)策略校驗(yàn)器的架構(gòu)圖如下: 圖 2:可組裝校驗(yàn)器的架構(gòu)圖stigirvalkUui instjrice : Stratanilatofa StrategrciMator () 悔 got 門譏:jv= )valida

45、te ()valirlate ()SL ci i2lL; ispisid: QclKktnbiais () 電胡毗*Bciiil O血(丨 j etCi:i f ():討Ikht囪弧時(shí) 勺I蜒mlkJbtHU卅I匚“上(0在本例中,我們并不是從整合遺留資產(chǎn)的角度出發(fā)的,在實(shí)際的例子中,銀行A 和銀行 B 可能都已存在各自的校驗(yàn)類,這些類的接口不會(huì)是一致的,而且返回類 型可能是 boolean 也可能是異常,甚至銀行 A 是 Java 應(yīng)用而銀行 B 是 C 應(yīng)用, 這樣的話我們必須將這些遺留應(yīng)用中已有的校驗(yàn)類適配成現(xiàn)在的接口,這里可以 對(duì)具體的 Strategy 實(shí)現(xiàn)類應(yīng)用 Adapter 模

46、式。模式與價(jià)值觀模式的三要素問(wèn)題,語(yǔ)境,解決方案我們?cè)谇懊嬉呀?jīng)論述過(guò)了,每個(gè)模式都有 它自己獨(dú)特的價(jià)值觀,那么這組架構(gòu)模式給我們帶來(lái)了什么?首先,它將校驗(yàn)邏輯從應(yīng)用邏輯中解耦出來(lái),使得應(yīng)用和校驗(yàn)器可以獨(dú)立變化。 第二,它促進(jìn)了代碼重用,校驗(yàn)器可以用到任何應(yīng)用邏輯中去,不必局限于一處。 如果需要的話,我們甚至可以將整個(gè)校驗(yàn)器當(dāng)作一個(gè)橫切關(guān)注點(diǎn) (Crosscut concern),應(yīng)用 A0P(Aspec t of Programming)技術(shù),將待校驗(yàn)數(shù)據(jù)當(dāng)作 Poin tcut, 這樣在應(yīng)用的代碼中會(huì)看不到任何校驗(yàn)代碼的痕跡,這就徹底分離出了數(shù)據(jù)校驗(yàn) 邏輯代碼。第三,從應(yīng)用場(chǎng)合來(lái)看,隔離校驗(yàn)

47、器主要用在那些數(shù)據(jù)類型簡(jiǎn)單而且 校驗(yàn)規(guī)則簡(jiǎn)單的數(shù)據(jù)校驗(yàn)中,可組裝校驗(yàn)器用在那些數(shù)據(jù)類型復(fù)雜或校驗(yàn)規(guī)則復(fù) 雜、多變的數(shù)據(jù)校驗(yàn)中,而動(dòng)態(tài)策略校驗(yàn)器則用在同一個(gè)數(shù)據(jù)的校驗(yàn)就有多種校 驗(yàn)規(guī)則策略的數(shù)據(jù)校驗(yàn)中??梢钥吹剑@幾種模式是根據(jù)待校驗(yàn)數(shù)據(jù)的粒度大小 和業(yè)務(wù)規(guī)則的復(fù)雜程度來(lái)劃分的。接下來(lái),我們研究一下模式的變體??山M裝校驗(yàn)器是這組模式的核心,它有很多 變體,其實(shí)動(dòng)態(tài)策略校驗(yàn)器就是它的一種變體,其他變體還有復(fù)合規(guī)則檢驗(yàn)器, 鏈?zhǔn)綑z驗(yàn)器,動(dòng)態(tài)注冊(cè)檢驗(yàn)器等。比如在 XML 校驗(yàn)器 SAX 的實(shí)現(xiàn)中,用戶可以 動(dòng)態(tài)地插入校驗(yàn)handle,或者我們需要對(duì)一個(gè)數(shù)據(jù)依次執(zhí)行多套校驗(yàn)規(guī)則,而 不像之前一次只有一個(gè)

48、校驗(yàn)規(guī)則會(huì)被執(zhí)行。對(duì)于這種需求,我們有三種方案可選, 第一種是復(fù)合規(guī)則檢驗(yàn)器,利用 Composite 模式來(lái)實(shí)現(xiàn)校驗(yàn)規(guī)則 IVRule 接口, 復(fù)合的校驗(yàn)規(guī)則類中包含一組簡(jiǎn)單的校驗(yàn)規(guī)則類VRule,當(dāng)調(diào)用復(fù)合類的 validate() 方法時(shí),復(fù)合類會(huì)依次調(diào)用所有的簡(jiǎn)單校驗(yàn)規(guī)則類。第二種是鏈 式校驗(yàn)器,利用 Chainofresponsibility 模式來(lái)實(shí)現(xiàn)校驗(yàn)規(guī)則 IVRule 接口, 前一個(gè)校驗(yàn)規(guī)則類執(zhí)行校驗(yàn)后傳遞到下一個(gè)校驗(yàn)規(guī)則類,一層層按固定順序傳遞 下去,每一個(gè)校驗(yàn)規(guī)則類關(guān)注的校驗(yàn)點(diǎn)不一樣,這適合于順序固定的情況。第三 種是動(dòng)態(tài)注冊(cè)校驗(yàn)器,利用 Registry 模式,將校驗(yàn)規(guī)則類 VRule 動(dòng)態(tài)地注冊(cè) 到校驗(yàn)器類 Validator 中去,比如注冊(cè)到一個(gè) List 或 Map 中,在校驗(yàn)器類的 validate() 方法中可以按某種算法來(lái)實(shí)現(xiàn)調(diào)用校驗(yàn)規(guī)則類的順序或更復(fù)雜的調(diào) 用邏輯。很顯然動(dòng)態(tài)注冊(cè)校驗(yàn)器很靈活,可擴(kuò)展性也很強(qiáng),但同時(shí)對(duì)校驗(yàn)器使用 者來(lái)說(shuō),它復(fù)

溫馨提示

  • 1. 本站所有資源如無(wú)特殊說(shuō)明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁(yè)內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒(méi)有圖紙預(yù)覽就沒(méi)有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫(kù)網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

評(píng)論

0/150

提交評(píng)論