HTTL (Hyper-Text Template Language) 是一個(gè)高性能的開(kāi)源JAVA模板引擎, 適用于動(dòng)態(tài)HTML頁(yè)面輸出, 可替代JSP頁(yè)面, 指令和Velocity相似。作者是阿里巴巴工程師梁飛,本文是在拜讀了HTTL的設(shè)計(jì)原則之后提煉出的部分通用設(shè)計(jì)原則。
按實(shí)體域,服務(wù)域,會(huì)話域劃分。
不管你做一個(gè)什么產(chǎn)品,都一定有一個(gè)被操作的主體,比如:服務(wù)框架管理的Service,任務(wù)框架管理的Task,Spring管理的Bean等,這就是實(shí)體域。
即然有被操作者,就一定有操作者,它管理被操作者的生命周期,發(fā)起動(dòng)作,比如:服務(wù)框架的ServiceInvoker,,任務(wù)框架的TaskScheduler,Spring的BeanFactory等,這就是服務(wù)域。
服務(wù)域發(fā)起動(dòng)作,在執(zhí)行過(guò)程中,會(huì)有一些臨時(shí)狀態(tài)需要存儲(chǔ)交換,比如:Invacation,Execution,Request等,這就是會(huì)話域。
相應(yīng)的,在HTTL中:
這樣劃分的好處是,職責(zé)清晰,可變狀態(tài)集中,每個(gè)域都是無(wú)鎖線程安全的,保證在大并發(fā)下,不會(huì)降低系統(tǒng)的活性。
這些核心領(lǐng)域模型也就是HTTL的API(Application Programming Interface),它是HTTL暴露給用戶的最少概念,也就是上面類(lèi)圖中的第一列。
按“微核 插件”體系組裝。
但凡有生命力的產(chǎn)品,都是在擴(kuò)展性方面設(shè)計(jì)的比較好的,因?yàn)闆](méi)有哪個(gè)產(chǎn)品可以覆蓋所有需求,對(duì)于開(kāi)源軟件尤其如此。
所以,產(chǎn)品只有具有良好的擴(kuò)展性,允許用戶或第三方參與進(jìn)來(lái),進(jìn)行二次開(kāi)發(fā),才能保持生命力。
怎么樣的擴(kuò)展性才是最好的?通常來(lái)講,就是沒(méi)有任何功能是硬編碼的,所有的功能都可被用戶替換。
那要如何才能做到這樣?一個(gè)重要的原則就是:平等對(duì)待第三方。
也就是凡是原作者能實(shí)現(xiàn)的功能,第三方也要能夠在不改變?cè)创a的前提下實(shí)現(xiàn)。
換言之,原作者應(yīng)把自己也當(dāng)作擴(kuò)展者,自己添加功能時(shí),也要用第三方擴(kuò)展者同樣的方式進(jìn)行,而不要有特權(quán)。
要做到這一點(diǎn),就需要一個(gè)良好的框架支撐,“微核 插件”是一個(gè)不錯(cuò)的選擇,Eclipse, Maven等知名軟件都采用該體系。
什么是“微核 插件”?微核,即最小化核心,內(nèi)核只負(fù)責(zé)插件的組裝,不帶任何功能邏輯,所有功能都由可替換的插件實(shí)現(xiàn),
并且,組裝過(guò)程應(yīng)基于統(tǒng)一的規(guī)則,比如基于setter注入,而不能對(duì)不同插件硬編碼組裝,這樣可以確保沒(méi)有任何功能在內(nèi)核中硬編碼。
比如:Spring, OSGI, JMX, ServiceLoader等都是常見(jiàn)的微核容器,它們負(fù)責(zé)基于統(tǒng)一規(guī)則的組裝,但不帶功能邏輯。
當(dāng)然,如果你不想帶這么重的框架,也可以自行實(shí)現(xiàn),HTTL就采用自行實(shí)現(xiàn)的httl.util.BeanFactory作為組裝微核。
在Engine.getEngine()中調(diào)用了BeanFatory.createBean(Engine.class, properties),
其中,properties即為httl.properties配置,BeanFatory基于setter方法,遞歸注入所有對(duì)象的屬性。
比如:httl.properties中配置了parser=httl.spi.parsers.CommentParser,
而DefaultEngine中有setParser(Parser parser)方法,就會(huì)被注入,并且Parser本身的屬性也會(huì)遞歸注入。
如果你需要擴(kuò)展或替換HTTL的實(shí)現(xiàn),請(qǐng)參見(jiàn):擴(kuò)展集成
既然非功能性的插件組裝過(guò)程,可以由微核框架來(lái)完成,那功能性的組裝怎么辦呢?
我們應(yīng)該把功能性的組裝過(guò)程也封裝成插件,即讓大插件組裝小插件,形成級(jí)聯(lián)組裝關(guān)系。
比如,HTTL的入口類(lèi)Engine的實(shí)例也是一個(gè)插件,它負(fù)責(zé)模板的緩存,加載,解析的總調(diào)度,即你可以替換DefaultEngine實(shí)現(xiàn)。
只需在httl.properties中配置:engine=com.your.YourEngine,可以將現(xiàn)有Parser等SPI注入你的Engine。
這些插件的接口,也就是HTTL的SPI(Service Provider Interface),它是HTTL暴露給擴(kuò)展者的最小粒度的替換單元,也就是上面類(lèi)圖中的第二列。
按復(fù)用度,抽象度,穩(wěn)定度分包。
穩(wěn)定度與抽象度關(guān)系如下圖:
也就是分包應(yīng)該如下:
其中上面那個(gè)包不依賴其它包。所以它很穩(wěn)定,應(yīng)盡量把抽象類(lèi)或接口放在這一層,
而下面那個(gè)包依賴了三個(gè)包,三個(gè)包變化都會(huì)引起它跟隨變化,所以它是不穩(wěn)定的,應(yīng)盡量把具體實(shí)現(xiàn)類(lèi)放在這一層。
因穩(wěn)定度與抽象度成正比,所以不穩(wěn)定度與抽象度成反比,用反比方便畫(huà)圖,計(jì)算方式如下:
應(yīng)該保持偏差越小越好,即下圖所示交點(diǎn)都落在綠色反比線左右:
基于上面的原則,HTTL的包結(jié)構(gòu)整體上劃分為三層:(對(duì)應(yīng)上面類(lèi)圖中的三列)
采用子包依賴父包風(fēng)格,所以將API放在根目錄,SPI接口獨(dú)立子包,各種實(shí)現(xiàn)放在SPI的下一級(jí)子包中。
下圖是HTTL所有包的不穩(wěn)定度與抽象度的比值距陣:(下圖為JDepend繪制)
HTTL所有核心包都是靠近反比線的,即上圖中用綠色標(biāo)識(shí)的點(diǎn),表示分包是合理的。
注:圖中黑色的點(diǎn)為util相關(guān)包,它們不抽象,卻被很多包依賴,只是內(nèi)部復(fù)用代碼,不影響整體設(shè)計(jì),用戶請(qǐng)不要依賴HTTL的util類(lèi)。
來(lái)源:http://www.icode9.com/content-4-120101.html聯(lián)系客服