一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

InfoQ: 案例研究:巴西國(guó)家醫(yī)療保健系統(tǒng)

 pstn 2007-12-12

案例研究:巴西國(guó)家醫(yī)療保健系統(tǒng)

作者 Fabiane Nardon, Floyd Marinescu譯者 沙曉蘭 發(fā)布于 2007年12月2日 上午9時(shí)0分

社區(qū)
Java
主題
故事和案例分析

巴西國(guó)家醫(yī)療保健系統(tǒng)曾被喻為全球最大的Java企業(yè)應(yīng)用,涉及200多萬(wàn)行代碼,囊括一個(gè)350個(gè)類(lèi)的區(qū)域模塊。該系統(tǒng)把所有能想象到全國(guó)范圍內(nèi)的各類(lèi)行政地區(qū)模塊化,其所實(shí)現(xiàn)的自動(dòng)化給公共醫(yī)療保健系統(tǒng)創(chuàng)造了巨大的價(jià)值,讓巴西人受益匪淺。本案例分析,從系統(tǒng)構(gòu)架、解決方案、教訓(xùn)與啟發(fā)以及項(xiàng)目的未來(lái)趨向等做全方面詳細(xì)深入的探索。

問(wèn)題域

巴西是世界上僅有的幾個(gè)提供完全免費(fèi)公共醫(yī)療保健系統(tǒng)的國(guó)家之一。和所有重大的公共服務(wù)設(shè)施一樣,它存在很多操作性問(wèn)題,原先很多工作都基于大量的紙質(zhì)文件之上,政府部門(mén)和地區(qū)醫(yī)療保健部門(mén)的IT系統(tǒng)間只有很小一部分實(shí)行了集成。例如:

  1. 醫(yī)療保健單位間沒(méi)有幫助病人到對(duì)方部門(mén)預(yù)約就診的調(diào)度系統(tǒng)。假如一個(gè)病人需要預(yù)約心臟科專(zhuān)家門(mén)診,他往往只能在多個(gè)專(zhuān)科門(mén)診前排長(zhǎng)隊(duì)做同樣的預(yù)約,直到排到其中一個(gè)能接受預(yù)約和治療。
  2. 缺乏病人過(guò)去的醫(yī)療信息,比如手術(shù)記錄、使用過(guò)的藥物紀(jì)錄和反應(yīng)等詳細(xì)病歷。
  3. 關(guān)于同一個(gè)病人的病歷常常在多個(gè)互不關(guān)聯(lián)的數(shù)據(jù)庫(kù)中被重復(fù)記錄多次。
  4. 由于像出生、死亡、醫(yī)療手術(shù)、疾病分析等重要統(tǒng)計(jì)數(shù)據(jù)分別存儲(chǔ)于各自的紙質(zhì)文件系統(tǒng)中,因此政府部門(mén)無(wú)法進(jìn)行任何醫(yī)療資源安排計(jì)劃,或?qū)Y源缺乏及時(shí)的應(yīng)急措施。
  5. 由于醫(yī)療政策法規(guī)的IT系統(tǒng)模塊沒(méi)有和保健系統(tǒng)站點(diǎn)集成,所以無(wú)法預(yù)測(cè)和防止醫(yī)療系統(tǒng)的腐敗。
  6. 許多當(dāng)?shù)蒯t(yī)生根本沒(méi)有任何信息系統(tǒng),缺乏調(diào)度,以至于所有病人都必須成天排隊(duì)等待醫(yī)治。

考慮到這么多問(wèn)題的存在,政府批準(zhǔn)創(chuàng)建一個(gè)自動(dòng)化醫(yī)療保健系統(tǒng),命名為Siga Saude。系統(tǒng)設(shè)計(jì)目標(biāo)是覆蓋所能想到的公共醫(yī)療保健信息系統(tǒng)的所有方面,包括會(huì)診調(diào)度,醫(yī)生和醫(yī)療器械的目錄清單管理,帳單,病理跟蹤,審核報(bào)告,規(guī)章制度的遵循,以及安全訪問(wèn)控制等。

系統(tǒng)首先在圣保羅市(巴西的最大城市,有近2000萬(wàn)人口,也是世界第4大城市)的所有醫(yī)療保健單位投入使用。如今,該應(yīng)用程序除了在圣保羅和其他20個(gè)城市中投入使用以外,也正逐漸覆蓋到巴西其他一些城市。除此之外,還有一些想要實(shí)現(xiàn)全國(guó)醫(yī)療保健系統(tǒng)自動(dòng)化的國(guó)家對(duì)此系統(tǒng)也產(chǎn)生濃厚的興趣,例如葡萄牙語(yǔ)國(guó)家安哥拉和莫桑比克等。

解決方案概述

該應(yīng)用開(kāi)發(fā)基于EJB2.1和Struts,采用成熟的EJB設(shè)計(jì)模式,引入數(shù)據(jù)傳送對(duì)象(data transfer objects)、會(huì)話外觀(session facade),服務(wù)定位( service locator),業(yè)務(wù)委托( business delegates),完美定義了一個(gè)層次分明的構(gòu)架。整個(gè)開(kāi)發(fā)在Eclipse環(huán)境下完成,測(cè)試和最后部署使用JBoss應(yīng)用服務(wù)器。在一些特定部分,也采用了Drools規(guī)測(cè)引擎(rules engine)。目前,應(yīng)用程序非集群地(non-clustered)部署運(yùn)行于Dual Xeon 3.1服務(wù)器之上,該服務(wù)器具有4GRAM,Linux操作系統(tǒng),并運(yùn)行JBoss 3.2.7。

從需求到開(kāi)發(fā),150多人參與了系統(tǒng)的功能定義工作,這些人中包括醫(yī)生和健康資訊專(zhuān)家。根據(jù)功能定義,研發(fā)團(tuán)隊(duì)最終把系統(tǒng)分解成以下這些模塊:


上述模塊圖中所標(biāo)出的每個(gè)模塊代碼行的數(shù)量均包括了服務(wù)層和與該模塊相對(duì)應(yīng)的域模型的代碼。另外,在服務(wù)層,有10萬(wàn)行代碼是模塊間共享,有57萬(wàn)行是各域模型間共享。代碼行的數(shù)量聽(tīng)上去有點(diǎn)嚇人,但實(shí)際上58%是自動(dòng)生成代碼,這在下文主題中關(guān)于注釋?zhuān)╝nnotation)的部分會(huì)討論到。

上述所有業(yè)務(wù)模型都包含JSP頁(yè)面,Struts Actions 和在服務(wù)層實(shí)現(xiàn)的業(yè)務(wù)委托。服務(wù)層包括EJB2.1的會(huì)話beans(session beans)和自動(dòng)生成的會(huì)話外觀(session facade)。域模塊包括pojo's和提供自動(dòng)生成實(shí)例Bean的注釋(annotation)。接下來(lái)會(huì)從URL到SQL一步一步詳細(xì)分析最后實(shí)現(xiàn)的調(diào)度系統(tǒng)。這里提到的系統(tǒng)模塊劃分、分層和其他的功能實(shí)現(xiàn)相一致,該模塊劃分圖實(shí)際上也幫助我們清晰地去了解該系統(tǒng)是如何構(gòu)建的。

經(jīng)驗(yàn)一:專(zhuān)家門(mén)診預(yù)約調(diào)度用例

無(wú)論從經(jīng)濟(jì)還是技術(shù)觀點(diǎn)出發(fā),該系統(tǒng)最重要的用例之一是描述一個(gè)地方小診所的前臺(tái)如何幫助病人預(yù)約專(zhuān)家門(mén)診。用例在調(diào)度模塊中實(shí)現(xiàn),同時(shí)牽涉到許多其他模塊。這個(gè)預(yù)約調(diào)度用例是這樣的:

首先,前臺(tái)在時(shí)間安排表中找到空的時(shí)間隙。這可以通過(guò)許多途徑查找,比如查找一個(gè)指定的醫(yī)生、專(zhuān)科、特定的醫(yī)療設(shè)備、醫(yī)療過(guò)程等等(點(diǎn)擊這里查看輸入界面), “getSlots”的序列圖參考下圖。當(dāng)他們找到某個(gè)空的時(shí)間隙(在序列圖上標(biāo)識(shí)為chooseSlot())以后,可以通過(guò)掃描病人醫(yī)療卡上的條形碼或者在病人忘了攜帶醫(yī)療卡的情況下通過(guò)輸入病人的姓名、出生日期或病人任何其他聯(lián)系方式查找到該病人的記錄,使用國(guó)家醫(yī)療保健卡模塊將病人簡(jiǎn)歷關(guān)聯(lián)到該時(shí)間隙(查看searchPatient())。然后,在同一界面上輸入所需的療程類(lèi)型,(參考第二個(gè)用戶輸入界面,序列圖上標(biāo)識(shí)為saveAppointment())。 如果診所沒(méi)有病人所需要的療程,可能的原因之一是診所沒(méi)有空的時(shí)間隙安排給病人預(yù)約就診,或者就是診所不提供這樣的療程,在這種情況下,前臺(tái)或者醫(yī)生可以幫助病人通過(guò)系統(tǒng)在其他診所預(yù)約合適的診療。

此序列圖描繪了一個(gè)典型用例在系統(tǒng)中的執(zhí)行過(guò)程。首先,一個(gè)Struts Action類(lèi)(ScheduleAction)執(zhí)行用戶請(qǐng)求并處理表示層邏輯。當(dāng)需要讀取數(shù)據(jù)或者需要引用業(yè)務(wù)規(guī)則的時(shí)候,Action類(lèi)調(diào)用實(shí)現(xiàn)了業(yè)務(wù)委托設(shè)計(jì)模式(ScheduleCF)的POJO對(duì)象的方法。這個(gè)類(lèi)從會(huì)話Facade類(lèi)(ScheduleCF)自動(dòng)生成,并將Web層從服務(wù)層使用的技術(shù)中獨(dú)立出來(lái)。那些實(shí)現(xiàn)了會(huì)話Facade設(shè)計(jì)模式(ScheduleCF)的會(huì)話Beans負(fù)責(zé)對(duì)其他類(lèi)執(zhí)行業(yè)務(wù)規(guī)則。例如,類(lèi)AppointmentService實(shí)現(xiàn)方法lockSlot()和searchForSlots(),當(dāng)用例需要保持?jǐn)?shù)據(jù)的時(shí)候,方法setAppointmentVO()在一個(gè)實(shí)例Bean上被調(diào)用。實(shí)例Bean機(jī)制不執(zhí)行數(shù)據(jù)庫(kù)查詢,但可以使用一個(gè)稱為Searcher的查詢優(yōu)化控件來(lái)彌補(bǔ)這個(gè)空缺。

會(huì)話Facade對(duì)象負(fù)責(zé)提供事務(wù)處理服務(wù)(transaction service)、數(shù)據(jù)庫(kù)連接和對(duì)應(yīng)用程序其他部分的安全服務(wù)。因?yàn)榉?wù)對(duì)象是POJO,它無(wú)法從Web層直接訪問(wèn)實(shí)例Beans,這時(shí)就需要加入一個(gè)會(huì)話bean層來(lái)幫忙了。

如果某個(gè)時(shí)間隙被鎖定,在會(huì)診預(yù)約安排數(shù)據(jù)庫(kù)中會(huì)存入相應(yīng)的時(shí)間戳和鎖定號(hào)(lock ID)。為了防止競(jìng)爭(zhēng)條件(race condition),這兩項(xiàng)數(shù)據(jù)通過(guò)一個(gè)SELECT FOR UPDATE命令保存。時(shí)間戳用來(lái)決定該預(yù)約是否過(guò)期,而儲(chǔ)存鎖定號(hào)的目的是為了避免對(duì)象試圖儲(chǔ)存過(guò)期預(yù)約的情況出現(xiàn)。當(dāng)對(duì)象請(qǐng)求儲(chǔ)存鎖定預(yù)約的時(shí)候,它會(huì)將收到的鎖定號(hào)和數(shù)據(jù)庫(kù)當(dāng)前鎖定號(hào)做比較,如果這兩個(gè)號(hào)不相同,那么這個(gè)新的診療預(yù)約信息不會(huì)被儲(chǔ)存。這樣一來(lái),即使某個(gè)鎖定發(fā)生在發(fā)送鎖定請(qǐng)求和診療預(yù)約儲(chǔ)存之間,都能被應(yīng)用系統(tǒng)偵測(cè)到且向用戶發(fā)送警告。該用例實(shí)現(xiàn)了一個(gè)邏輯鎖定。無(wú)論是數(shù)據(jù)庫(kù)還是EJB鎖定都不需要很長(zhǎng)的時(shí)間,利于實(shí)現(xiàn)業(yè)務(wù)規(guī)則。

該用例中涉及到的域?qū)ο蟮暮?jiǎn)化類(lèi)圖如下:

經(jīng)驗(yàn)二:應(yīng)用注釋(annotation)生成代碼

項(xiàng)目初期,開(kāi)發(fā)團(tuán)隊(duì)采用XDoclet1來(lái)生成部署描述器(deployment descriptors),值對(duì)象(value objects),查詢,會(huì)話facades,Struts表和動(dòng)作,值驗(yàn)證,以及本地和遠(yuǎn)程EJB接口等。由于項(xiàng)目的短時(shí)間開(kāi)發(fā)周期(short time frame)計(jì)劃,需要應(yīng)用一些額外技術(shù)幫助開(kāi)發(fā)團(tuán)隊(duì)在短時(shí)間內(nèi)和保證減少bug的前提下盡可能多產(chǎn),代碼生成也就成為這個(gè)項(xiàng)目中的一個(gè)關(guān)鍵策略。也因?yàn)樗年P(guān)鍵性,最初的XDoclets模板后來(lái)被修改生成“專(zhuān)家代碼”。

起初的10個(gè)月里,XDoclet方式在項(xiàng)目進(jìn)行中很適用,幫助開(kāi)發(fā)團(tuán)隊(duì)達(dá)到了所需的開(kāi)發(fā)能力,但沒(méi)過(guò)多久,隨著代碼的不斷增多,生成代碼的速度卻越來(lái)越慢,以至于生產(chǎn)率慘遭嚴(yán)重影響。于是,開(kāi)發(fā)團(tuán)隊(duì)決定采用另一種代碼生成策略,這就是Java 5 注釋 (annotations)。

注釋策略可以解決XDoclet的以下幾項(xiàng)缺點(diǎn):

  • 注釋允許像部署描述器這樣漸增的代碼生成文件:XDoclet中,假使一個(gè)類(lèi)被修改,XDoclet需要再次讀取所有類(lèi)才能生成新的部署描述器。但使用注釋的話,可以僅僅重新生成部署描述器中由于類(lèi)被修改而受影響的部分。鑒于大部分時(shí)候,開(kāi)發(fā)人員只修改很少一部分類(lèi),注釋策略的采用就幫助省去很多時(shí)間。
  • Xdoclet在生成文件的過(guò)程中需要訪問(wèn)子組件的源代碼,這增加了連接和構(gòu)建的復(fù)雜度。所有開(kāi)發(fā)人員需要同樣的權(quán)限訪問(wèn)所有源代碼,而非如最初計(jì)劃那樣僅訪問(wèn)編譯后的jar包。但采用注釋的話,就可以避免這個(gè)問(wèn)題。
  • 注釋可以在運(yùn)行時(shí)(runtime)被處理,除了生成代碼,這個(gè)特點(diǎn)可以帶來(lái)更多好處。

團(tuán)隊(duì)為EJB 3.0 注釋和其他一些自定義注釋開(kāi)發(fā)了處理器,使其最后生成的代碼和采用XDoclet時(shí)所生成代碼的相似。這些生成的代碼在EJB 2.1下也同樣兼容。

注釋處理器使用Velocity模板。從XDoclet轉(zhuǎn)移到自己編寫(xiě)使用的模板中,最大的挑戰(zhàn)是構(gòu)建和XDoclet提供的同樣容易理解的一組模板和幫助類(lèi)。

采用注釋以后,代碼生成時(shí)間縮短很多。使用XDoclet,如果其中一個(gè)類(lèi)有所修改,需要1分50秒的時(shí)間從400個(gè)類(lèi)中重新生成代碼,但是使用APT以后,同樣的操作僅需10秒鐘。

經(jīng)驗(yàn)三:規(guī)則引擎如何簡(jiǎn)化業(yè)務(wù)邏輯

根據(jù)政府立法,一些業(yè)務(wù)邏輯一直在修改中,有些按城市各異,系統(tǒng)某些部分不得不處理這些特殊的業(yè)務(wù)邏輯。比方說(shuō),某診所想要提供x光透視服務(wù),這等價(jià)于幾項(xiàng)服務(wù)規(guī)則,擁有x光透視設(shè)備,有授權(quán)資格的放射科醫(yī)生等。由于這些規(guī)則經(jīng)常在變,所以最好把它們留在主代碼庫(kù)外頭,這樣即使規(guī)則被修改也不用去修改代碼。

在SIGA-Saude系統(tǒng)中,因?yàn)橐肓?a href="http:///">Drools 規(guī)則引擎而使這個(gè)難題迎刃而解。Drools實(shí)現(xiàn)了JSR-94-Java 規(guī)則引擎應(yīng)用程序接口。規(guī)則由SIGA-Saude系統(tǒng)中一個(gè)稱作Decision的控件來(lái)處理。該控件工作于一組規(guī)則組和一個(gè)工作記憶區(qū)(working memory)。規(guī)則組工作于一組消息,每條規(guī)則都相應(yīng)有一條由實(shí)現(xiàn)了br.com.vidatis.common.decision.message.RuleMessage接口的類(lèi)描述的消息。當(dāng)一條規(guī)則或是規(guī)則組的子集被滿足的時(shí)候,對(duì)應(yīng)的消息會(huì)從隊(duì)列中移除,這樣做可以追蹤規(guī)則的處理。如果最后在隊(duì)列中沒(méi)有任何消息剩下,表明規(guī)則組中的所有的規(guī)則都被滿足。任何一條規(guī)則都可以觸發(fā)許多不同的動(dòng)作,也可以觸發(fā)新的規(guī)則組,規(guī)則在XML文件中描述,然后交由Drools引擎處理。

XML文件范例:

<rule name="if_clinic_code_then_checksum_digit_valid">
<parameter identifier="clinic">
<java:class>br.atech.smssp.domain.clinic.vo.ClinicVO</java:class>

</parameter>
<java:condition>clinic.getCode() != null</java:condition>
<java:condition>!clinic.getCode().equals("")</java:condition>
<java:consequence>
//clinic code should be mandatory and valid
if(br.com.vidatis.common.decision.rule.CodeRule.isValidCone(clinic.getCode())){
ruleMessage.markRuleAsValid("if_clinic_code_then_checksum_digit_valid");
}
</java:consequence>

</rule>

<rule name="if_maintainer_code_then_checksum_digit_valid">
<parameter identifier="clinic">
<java:class>br.atech.smssp.domain.clinic.vo.ClinicVO</java:class>
</parameter>
<java:condition>clinic.getMaintainerCode() != null</java:condition>

<java:condition>!clinic.getMaintainerCode().equals("")</java:condition>
<java:consequence>
//Maintainer code is optional, but should be valid if it is informed.
if(br.com.vidatis.common.decision.rule.CodeRule.isValidCode(clinic.getMaintainerCode())){
ruleMessage.markRuleAsValid("if_maintainer_code_then_checksum_digit_valid");
}
</java:consequence>
</rule>

<rule name="if_maintainer_code_then_checksum_digit_valid_OPTIONAL_EMPTY">
<parameter identifier="clinic">

<java:class>br.atech.smssp.domain.clinic.vo.ClinicVO</java:class>
</parameter>
<java:condition>clinic.getMaintainerCode() != null</java:condition>
<java:condition>clinic.getMaintainerCode().equals("")</java:condition>
<java:consequence>

//Maintainer code is optional and can be empty
ruleMessage.markRuleAsValid("if_maintainer_code_then_checksum_digit_valid");
</java:consequence>
</rule>

<rule name="if_maintainer_code_then_checksum_digit_valid_OPTIONAL_NULL">
<parameter identifier="clinic">
<java:class>br.atech.smssp.domain.clinic.vo.ClinicVO</java:class>

</parameter>
<java:condition>clinic.getMaintainerCode() == null</java:condition>
<java:consequence>
//Maintainer code is optional and can be NULL
ruleMessage.markRuleAsValid("if_maintainer_code_then_checksum_digit_valid");
</java:consequence>
</rule>

這個(gè)例子中,保存某診所數(shù)據(jù)的時(shí)候,服務(wù)類(lèi)會(huì)調(diào)用Drools規(guī)則引擎檢查和認(rèn)證所有的規(guī)則,數(shù)據(jù)只有符合所有的規(guī)則才能被儲(chǔ)存。這些認(rèn)證規(guī)則隨著政府制定的規(guī)章制度的修改而變化。由于修改經(jīng)常發(fā)生,所以把它們和應(yīng)用程序代碼劃分開(kāi)來(lái)非常重要,這樣才能在維護(hù)和修改程序的時(shí)候不需要重啟整個(gè)應(yīng)用系統(tǒng)。

教訓(xùn)與啟發(fā)

代碼生成在這個(gè)項(xiàng)目中是成功的關(guān)鍵,它不但提高了開(kāi)發(fā)人員的生產(chǎn)率,同時(shí)也保證了代碼風(fēng)格一致。在50個(gè)開(kāi)發(fā)人員在各個(gè)獨(dú)立團(tuán)隊(duì)中各自工作且共享基礎(chǔ)組件的情況下,使他們保持代碼一致性非常困難。起初采用XDoclet,然而好景不長(zhǎng),由于生成文件時(shí)間越來(lái)越長(zhǎng),對(duì)開(kāi)發(fā)速度帶來(lái)出乎意料的壞影響。之后,轉(zhuǎn)而采用注釋策略,才大大地減短了生成時(shí)間,真正體現(xiàn)了生成代碼的優(yōu)越性。

好的團(tuán)隊(duì)交流是必須的。特別是像這樣的大項(xiàng)目,總會(huì)有些時(shí)候有些人發(fā)表不同的意見(jiàn)。讓所有的人互相理解對(duì)方的觀點(diǎn)實(shí)在很難。特別是在這樣一群人中,交流真的是讓人頭疼的難題:

  • 開(kāi)發(fā)者 VS. 構(gòu)架師——你不得不吃你自己的狗食。當(dāng)構(gòu)架師積極參與到開(kāi)發(fā)工作中來(lái)的時(shí)候,他們可以提出更好的建議,提高生產(chǎn)率。同時(shí)團(tuán)隊(duì)的“開(kāi)心指數(shù)”也相對(duì)得到提升,同樣也影響到整體的開(kāi)發(fā)能力。
  • 需求分析者/客戶 VS. 開(kāi)發(fā)人員/構(gòu)架師——這兩組人交流的失敗可以直接導(dǎo)致項(xiàng)目的推遲并讓所有人灰心喪氣。好的規(guī)格說(shuō)明是必要的,但要這兩組人理解雙方的動(dòng)機(jī),僅靠一個(gè)好的用例說(shuō)明來(lái)使他們?nèi)谇⒌孟褚粋€(gè)團(tuán)隊(duì)那樣工作簡(jiǎn)直是不可能的。
  • 出類(lèi)拔萃的開(kāi)發(fā)人員 VS. 其他出類(lèi)拔萃的開(kāi)發(fā)人員——這個(gè)項(xiàng)目中有個(gè)開(kāi)發(fā)小組,擁有非常優(yōu)秀的開(kāi)發(fā)人員,他們中的每個(gè)人都有滿腦子的好點(diǎn)子來(lái)開(kāi)發(fā)軟件。然而在短時(shí)開(kāi)發(fā)周期中,這個(gè)組的組員每天都冒出一些無(wú)法驗(yàn)證可能性的改革性的主意,這個(gè)小組因此受到一些挫折。其實(shí)在像這樣的項(xiàng)目中,重要的是堅(jiān)持定義嚴(yán)謹(jǐn)?shù)慕K極目標(biāo),并使每個(gè)人都能正確理解作出每個(gè)決定的理由。這個(gè)開(kāi)發(fā)小組后來(lái)采取間斷性的重構(gòu)周(refactor),在這些重構(gòu)周里,組員提出系統(tǒng)中存在的問(wèn)題,討論如何解決這些問(wèn)題并在這段時(shí)間里對(duì)系統(tǒng)進(jìn)行重構(gòu)。整個(gè)星期都只作重構(gòu),沒(méi)有任何新代碼的開(kāi)發(fā),只用來(lái)修改原有代碼使之更便于實(shí)現(xiàn)未來(lái)將冒出來(lái)的新點(diǎn)子而提高工作效率。

如果你的構(gòu)架定義確切,有規(guī)則、有代碼生成、套用設(shè)計(jì)模式、構(gòu)件的話,就比較容易進(jìn)行小范圍或中等程度的軟件重構(gòu),大范圍重構(gòu)往往很麻煩。然而,現(xiàn)實(shí)系統(tǒng)的部分構(gòu)架不夠確切,嚴(yán)格的規(guī)則更使得20%的應(yīng)用程序那以實(shí)現(xiàn)。明確你究竟能夠在加強(qiáng)構(gòu)架標(biāo)準(zhǔn)這點(diǎn)上走多遠(yuǎn)至關(guān)重要。項(xiàng)目開(kāi)發(fā)過(guò)程中,有時(shí)候就算一些解決方案顯然不是最好的,開(kāi)發(fā)人員也只是強(qiáng)行將它們向構(gòu)架靠攏,而非重新探討最佳方案。另一方面,系統(tǒng)中某些部分,規(guī)則要求并不嚴(yán)格,比如說(shuō)用戶界面,對(duì)于這些部分,開(kāi)發(fā)人員則各取其愛(ài),以至于造成系統(tǒng)這些部分質(zhì)量較差、代碼不易管理等預(yù)料未及的壞結(jié)果。

在龐大的應(yīng)用程序中,面對(duì)繁雜的依賴性和代碼,開(kāi)發(fā)周期往往不得不延長(zhǎng)。有時(shí)候,在某個(gè)界面上加上一個(gè)簡(jiǎn)單的域(field)就意味著系統(tǒng)的許多部分都隨之需要修改,這很費(fèi)時(shí)間。無(wú)論是開(kāi)發(fā)人員還是客戶都因此感到頭痛,工具變得太過(guò)沉重以至于所有一切都比原本更花時(shí)間。把應(yīng)用程序劃分成許多構(gòu)件,還是不足以徹底解決問(wèn)題。盡管J2EE有很多優(yōu)點(diǎn),但同時(shí)代碼也更加復(fù)雜,生產(chǎn)率還是受到影響,這些很難跟管理人員和客戶解釋清楚。

項(xiàng)目啟動(dòng)最初,很多人都說(shuō)按照那樣的短時(shí)開(kāi)發(fā)周期,項(xiàng)目根本沒(méi)有辦法完成。在其他一些國(guó)家的公共醫(yī)療保健系統(tǒng)開(kāi)發(fā)過(guò)程中,也都發(fā)生過(guò)太多太多令人汗顏的經(jīng)驗(yàn)。所以,從這個(gè)項(xiàng)目中得到的最后一個(gè)啟示是:當(dāng)別人說(shuō)某件事不可能的時(shí)候,千萬(wàn)不要讓他們這樣的泛泛之說(shuō)影響了你去將這件事變得可能。

未來(lái)方向

容器外測(cè)試

無(wú)法進(jìn)行容器外測(cè)試讓開(kāi)發(fā)團(tuán)隊(duì)很困擾,無(wú)法在初期就進(jìn)行容器外測(cè)試的原因是他們被迫從合法的代碼開(kāi)始工作。政府制定的規(guī)則和規(guī)章條例模塊是一個(gè)巨大的EJB 1.0系統(tǒng)。結(jié)合代碼生成技術(shù)來(lái)處理所有的實(shí)例bean和其他容器假象(container artifact),程序的新生部分變的越來(lái)越基于POJO。但將他們部署到容器中以后,甚至只是一個(gè)漸進(jìn)變化也緩慢到開(kāi)發(fā)人員無(wú)法接受的地步。

遷移到基于POJO的構(gòu)架

目前的系統(tǒng)明顯有大量的容器依賴和EJB樣板代碼(plumbing code)(盡管大部分都隱藏在生成的業(yè)務(wù)委托和會(huì)話外觀中),但開(kāi)發(fā)團(tuán)隊(duì)計(jì)劃的遷移方向是完完全全基于POJO,包括業(yè)務(wù)規(guī)則,他們計(jì)劃將所有的會(huì)話Bean重構(gòu)成一個(gè)單一的會(huì)話bean interceptor,由這個(gè)單一的會(huì)話bean interceptor專(zhuān)門(mén)向服務(wù)層調(diào)用。構(gòu)建這個(gè)單一的會(huì)話bean interceptor的目的在于提供像事務(wù)(transaction)和線程管理這樣的中間件服務(wù)。他們?cè)仍O(shè)想在修改之后的構(gòu)架上采用Spring,但這注定開(kāi)銷(xiāo)太過(guò)昂貴。鑒于當(dāng)時(shí)擁有的會(huì)話bean都已經(jīng)全部生成,相比較從EJB中完全引身而退來(lái)說(shuō),修改代碼生成邏輯來(lái)貫徹會(huì)話bean interceptor明顯簡(jiǎn)單得多。將這樣的重構(gòu)變得可能的另一個(gè)元素是現(xiàn)存的業(yè)務(wù)委托層可以將這些大幅修改從Web端屏蔽。

為什么他們沒(méi)有一并擺脫掉EJB呢?理由是最后他們必須將應(yīng)用程序展示給在醫(yī)療衛(wèi)生部門(mén)的另外的團(tuán)隊(duì),這些團(tuán)隊(duì)需要訪問(wèn)該系統(tǒng),并正準(zhǔn)備簡(jiǎn)單地讓工作人員(從圣保羅市)接手開(kāi)始投入使用可行的業(yè)務(wù)委托。由于業(yè)務(wù)委托隱藏了遠(yuǎn)程的各方面,他們知道合作方將不會(huì)工作于Java上,所以預(yù)期系統(tǒng)集成會(huì)非常順利。

不同的事務(wù)類(lèi)型又將如何處理呢?不同的事務(wù)界限有不同的方法,目標(biāo)方法和事務(wù)類(lèi)型間有一個(gè)映射,所以他們知道該執(zhí)行哪個(gè)方法來(lái)處理相應(yīng)的事務(wù)。順便提一句,這里的會(huì)話Bean interceptor和事務(wù)策略同F(xiàn)loyd Marinescu在EJB設(shè)計(jì)模式一書(shū)中描述的極為相近。 :)

最終,他們實(shí)在是想擺脫掉實(shí)例Bean,也許甚至想直接只保存值對(duì)象和Hibernate。面對(duì)這樣修改的潛在可能性最大花銷(xiāo)是測(cè)試,他們必須徹徹底底地測(cè)試系統(tǒng)的每個(gè)方面,單一的單元測(cè)試無(wú)法覆蓋全部。

AJAX簡(jiǎn)化Web用戶界面

AJAX被公認(rèn)是為非技術(shù)終端用戶簡(jiǎn)化用戶界面工作流程的有效工具。就如在調(diào)度用例中,自動(dòng)補(bǔ)全(auto-completion)可以幫助輸入醫(yī)生姓名,療程名稱,專(zhuān)科名稱,實(shí)在沒(méi)有理由去調(diào)用彈出窗口來(lái)做查詢,從長(zhǎng)長(zhǎng)的列單中去尋找這些名字。另外,在使用大而冗長(zhǎng)的表單時(shí),使用AJAX一步一步將表單域的值保存到HTTPSession中去無(wú)疑會(huì)更好,還能防止一些用戶因不小心錯(cuò)關(guān)頁(yè)面而遺失所輸入的信息。

最近AJAX也被部署項(xiàng)目中,被用來(lái)瀏覽一個(gè)需要讀取數(shù)據(jù)的巨大的流程樹(shù),幫助我們?cè)诳s短了數(shù)據(jù)錄入時(shí)間的同時(shí)也給終端用戶提供更漂亮的界面。點(diǎn)擊這里查看界面截圖。

查看英文原文:Casestudy: Brasilian National Healthcare System
加入書(shū)簽
digg+,
reddit+,
del.+,
dzone+
標(biāo)簽
Struts,
JBoss,
注釋,
EJB

7 條回復(fù)

回復(fù)

little mistake 發(fā)表人 曉 汪 發(fā)表于 2007年12月2日 下午6時(shí)26分
Re: little mistake 發(fā)表人 霍 泰穩(wěn) 發(fā)表于 2007年12月2日 下午9時(shí)52分
Re: little mistake 發(fā)表人 曉 汪 發(fā)表于 2007年12月2日 下午10時(shí)15分
擁有2億(應(yīng)該為2000萬(wàn))多人口的巴西最大的城市,也是世界第4大城市 發(fā)表人 wander zhou 發(fā)表于 2007年12月3日 下午6時(shí)29分
Re: 擁有2億(應(yīng)該為2000萬(wàn))多人口的巴西最大的城市,也是世界第4大城市 發(fā)表人 sihai T 發(fā)表于 2007年12月3日 下午7時(shí)55分
Re: 擁有2億(應(yīng)該為2000萬(wàn))多人口的巴西最大的城市,也是世界第4大城市 發(fā)表人 霍 泰穩(wěn) 發(fā)表于 2007年12月3日 下午8時(shí)15分
Re: 擁有2億(應(yīng)該為2000萬(wàn))多人口的巴西最大的城市,也是世界第4大城市 發(fā)表人 涼粉 小刀 發(fā)表于 2007年12月4日 上午5時(shí)45分
  1. 返回頂部

    little mistake

    2007年12月2日 下午6時(shí)26分 發(fā)表人 曉 汪

    在"經(jīng)驗(yàn)三:規(guī)則引擎如何簡(jiǎn)化業(yè)務(wù)邏輯"的內(nèi)容中,jsp-94是否應(yīng)為 jsr-94? 還有,可否發(fā)原文鏈接?謝謝

  2. 返回頂部

    Re: little mistake

    2007年12月2日 下午9時(shí)52分 發(fā)表人 霍 泰穩(wěn)

    經(jīng)核實(shí),確實(shí)是“JSR-94”(一個(gè)Java規(guī)則引擎),已經(jīng)做了修正,并補(bǔ)全了原文鏈接。謝謝汪曉朋友的指正!

  3. 返回頂部

    Re: little mistake

    2007年12月2日 下午10時(shí)15分 發(fā)表人 曉 汪

    ^_^多謝

  4. 2億人口的城市太可怕了。

  5. “擁有2億多人口的巴西最大的城市”應(yīng)該是“擁有2億多人口的巴西的最大城市”

  6. 這句話確實(shí)有歧義,已經(jīng)根據(jù)Sihai T朋友的建議對(duì)原文做了修改。非常感謝wander zhou和sihai T朋友的指正!

  7. 2000萬(wàn)那里還要改一下的

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多

    免费播放一区二区三区四区| 亚洲熟妇av一区二区三区色堂| 永久福利盒子日韩日韩| 激情丁香激情五月婷婷| 欧美一区二区在线日韩| 午夜精品麻豆视频91| 国产精品二区三区免费播放心| 日本不卡在线视频你懂的| 亚洲乱码av中文一区二区三区| 黄色片国产一区二区三区| 国产欧美韩日一区二区三区| 日韩欧美一区二区久久婷婷 | 精品偷拍一区二区三区| 国产日韩久久精品一区| 精品少妇人妻av一区二区蜜桃| 一区二区三区日韩经典| 少妇肥臀一区二区三区| 激情爱爱一区二区三区| 亚洲精品一区二区三区免| 我想看亚洲一级黄色录像| 国产三级不卡在线观看视频| 国产亚洲欧美日韩国亚语| 欧美日韩黑人免费观看| 欧洲偷拍视频中文字幕| 91天堂素人精品系列全集| 国产伦精品一区二区三区高清版| 插进她的身体里在线观看骚| 国产精品一区二区有码| 日本加勒比系列在线播放| 91亚洲精品国产一区| 91久久国产福利自产拍| 九九热这里只有精品视频| 大香伊蕉欧美一区二区三区| 国产日本欧美特黄在线观看| 欧美一区二区黑人在线| 久久精品国产99精品亚洲| 在线日韩欧美国产自拍| 91超频在线视频中文字幕| 日本少妇三级三级三级| 亚洲欧美日韩国产自拍| 一级片黄色一区二区三区|