小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

從HTTL模板引擎看軟件設(shè)計原則

 印度阿三17 2019-02-22

HTTL (Hyper-Text Template Language) 是一個高性能的開源JAVA模板引擎, 適用于動態(tài)HTML頁面輸出, 可替代JSP頁面, 指令和Velocity相似。作者是阿里巴巴工程師梁飛,本文是在拜讀了HTTL的設(shè)計原則之后提煉出的部分通用設(shè)計原則。

模型劃分原則

按實體域,服務(wù)域,會話域劃分。

不管你做一個什么產(chǎn)品,都一定有一個被操作的主體,比如:服務(wù)框架管理的Service,任務(wù)框架管理的Task,Spring管理的Bean等,這就是實體域。

即然有被操作者,就一定有操作者,它管理被操作者的生命周期,發(fā)起動作,比如:服務(wù)框架的ServiceInvoker,,任務(wù)框架的TaskScheduler,Spring的BeanFactory等,這就是服務(wù)域。

服務(wù)域發(fā)起動作,在執(zhí)行過程中,會有一些臨時狀態(tài)需要存儲交換,比如:Invacation,Execution,Request等,這就是會話域。

相應(yīng)的,在HTTL中:

  • Engine 為服務(wù)域
    • 它是API的入口,并負責實體域Template的生命周期管理,它是Singleton單一實例的,加載后不可變,所以是線程安全的,它的初始化過程較重,請復用單例。
  • Template 為實體域
    • 代表著被操作者,它是Prototype原型實例的,即每個模板產(chǎn)生一個實例,加載后不可變,同樣也是線程安全的,模板變化后,將產(chǎn)生不同的實例,而不改變原實例。
  • Context 為會話域
    • 持有操作過程中的所有可變狀態(tài),它是ThreadLocal線程內(nèi)實例的,即不和其它線程競爭使用,所以也是線程安全的,請不要跨線程傳遞,它的初始化過程很輕量,每次模板執(zhí)行前都新建實例,執(zhí)行完即銷毀。

這樣劃分的好處是,職責清晰,可變狀態(tài)集中,每個域都是無鎖線程安全的,保證在大并發(fā)下,不會降低系統(tǒng)的活性。

這些核心領(lǐng)域模型也就是HTTL的API(Application Programming Interface),它是HTTL暴露給用戶的最少概念,也就是上面類圖中的第一列。

擴展點組裝原則

按“微核 插件”體系組裝。

但凡有生命力的產(chǎn)品,都是在擴展性方面設(shè)計的比較好的,因為沒有哪個產(chǎn)品可以覆蓋所有需求,對于開源軟件尤其如此。

所以,產(chǎn)品只有具有良好的擴展性,允許用戶或第三方參與進來,進行二次開發(fā),才能保持生命力。

怎么樣的擴展性才是最好的?通常來講,就是沒有任何功能是硬編碼的,所有的功能都可被用戶替換。

那要如何才能做到這樣?一個重要的原則就是:平等對待第三方。

也就是凡是原作者能實現(xiàn)的功能,第三方也要能夠在不改變源代碼的前提下實現(xiàn)。

換言之,原作者應(yīng)把自己也當作擴展者,自己添加功能時,也要用第三方擴展者同樣的方式進行,而不要有特權(quán)。

要做到這一點,就需要一個良好的框架支撐,“微核 插件”是一個不錯的選擇,Eclipse, Maven等知名軟件都采用該體系。

什么是“微核 插件”?微核,即最小化核心,內(nèi)核只負責插件的組裝,不帶任何功能邏輯,所有功能都由可替換的插件實現(xiàn),

并且,組裝過程應(yīng)基于統(tǒng)一的規(guī)則,比如基于setter注入,而不能對不同插件硬編碼組裝,這樣可以確保沒有任何功能在內(nèi)核中硬編碼。

比如:Spring, OSGI, JMX, ServiceLoader等都是常見的微核容器,它們負責基于統(tǒng)一規(guī)則的組裝,但不帶功能邏輯。

當然,如果你不想帶這么重的框架,也可以自行實現(xiàn),HTTL就采用自行實現(xiàn)的httl.util.BeanFactory作為組裝微核。

在Engine.getEngine()中調(diào)用了BeanFatory.createBean(Engine.class, properties),

其中,properties即為httl.properties配置,BeanFatory基于setter方法,遞歸注入所有對象的屬性。

比如:httl.properties中配置了parser=httl.spi.parsers.CommentParser,

而DefaultEngine中有setParser(Parser parser)方法,就會被注入,并且Parser本身的屬性也會遞歸注入。

如果你需要擴展或替換HTTL的實現(xiàn),請參見:擴展集成

既然非功能性的插件組裝過程,可以由微核框架來完成,那功能性的組裝怎么辦呢?

我們應(yīng)該把功能性的組裝過程也封裝成插件,即讓大插件組裝小插件,形成級聯(lián)組裝關(guān)系。

比如,HTTL的入口類Engine的實例也是一個插件,它負責模板的緩存,加載,解析的總調(diào)度,即你可以替換DefaultEngine實現(xiàn)。

只需在httl.properties中配置:engine=com.your.YourEngine,可以將現(xiàn)有Parser等SPI注入你的Engine。

這些插件的接口,也就是HTTL的SPI(Service Provider Interface),它是HTTL暴露給擴展者的最小粒度的替換單元,也就是上面類圖中的第二列。

整體分包原則

按復用度,抽象度,穩(wěn)定度分包。

  • 復用度:
    • 每種用戶所需用到的類,就是同一復用粒度的,比如:使用者和擴展者,這樣可以減少代碼干擾,以及最大化復用。
  • 穩(wěn)定度:
    • 被依賴包和依賴包的占比,如果一個包依賴很多包,那別的包變化都會引起它跟隨變化,所以它就不穩(wěn)定,反之即穩(wěn)定, 保持被依賴者總是比依賴者的穩(wěn)定度高,形成金子塔關(guān)系,這樣可以防止不穩(wěn)定性傳染,比如a包只依賴3個包,而b包依賴10個包,那就不要讓a包去依賴b包。
  • 抽象度:
    • 包中抽象類個數(shù)占比,比如包中有10個類,其中3個為抽象類(包括接口),則抽象度為3/10, 保持包的穩(wěn)定度和抽象度成正比,即把抽象類(包括接口)放到穩(wěn)定的包中,把具體實現(xiàn)類放到不穩(wěn)定的包中,這樣可以保持每層都有足夠的擴展性。

穩(wěn)定度與抽象度關(guān)系如下圖:

也就是分包應(yīng)該如下:

其中上面那個包不依賴其它包。所以它很穩(wěn)定,應(yīng)盡量把抽象類或接口放在這一層,

而下面那個包依賴了三個包,三個包變化都會引起它跟隨變化,所以它是不穩(wěn)定的,應(yīng)盡量把具體實現(xiàn)類放在這一層。

因穩(wěn)定度與抽象度成正比,所以不穩(wěn)定度與抽象度成反比,用反比方便畫圖,計算方式如下:

  • (1) I = Ce / (Ca Ce)
    • I: Instability (不穩(wěn)定度)
    • Ca: Afferent Coupling (傳入依賴,也就是被其它包依賴的個數(shù))
    • Ce: Efferent Coupling (輸出依賴,也就是依賴其它包的個數(shù))
  • (2) A = Na / Nc
    • A: Abstractness (抽象度)
    • Na: Number of abstract classes (抽象類的個數(shù))
    • Nc: Number of classes (類的個數(shù),包括抽象類)
  • (3) D = abs(1 - I - A) * sin(45)
    • D: Distance (偏差)
    • I: Instability (不穩(wěn)定度)
    • A: Abstractness (抽象度)

應(yīng)該保持偏差越小越好,即下圖所示交點都落在綠色反比線左右:

基于上面的原則,HTTL的包結(jié)構(gòu)整體上劃分為三層:(對應(yīng)上面類圖中的三列)

  • API (Application Programming Interface)
    • 模板引擎的使用者依賴的接口類,也是核心領(lǐng)域模型所在,保持最少概念,并隱藏實現(xiàn)細節(jié),其中Engine類相當于微內(nèi)核,只管理非功能性的擴展點的加載,不硬編碼模板加載解析渲染的任何部分。
  • SPI (Service Provider Interface)
    • 模板引擎的擴展者依賴的接口類,它依賴于API的領(lǐng)域模型,它是模板引擎功能正交分解的抽象層,以保證用戶可以最小粒度替換需要改寫的地方,方便二次開發(fā)。
  • BUILT-IN (Built-in Implementation)
    • 內(nèi)置擴展實現(xiàn),它是SPI標準實現(xiàn),也是可被用戶替換的類,它包含引擎所有做的事,包括擴展點之間的組裝過程(可替換DefaultEngine),以確保沒有功能換不掉,即平等對待擴展者。

采用子包依賴父包風格,所以將API放在根目錄,SPI接口獨立子包,各種實現(xiàn)放在SPI的下一級子包中。

  • 使用者API導入:import httl.*;
  • 擴展者SPI導入:import httl.spi.*;

下圖是HTTL所有包的不穩(wěn)定度與抽象度的比值距陣:(下圖為JDepend繪制)

HTTL所有核心包都是靠近反比線的,即上圖中用綠色標識的點,表示分包是合理的。

注:圖中黑色的點為util相關(guān)包,它們不抽象,卻被很多包依賴,只是內(nèi)部復用代碼,不影響整體設(shè)計,用戶請不要依賴HTTL的util類。

來源:http://www./content-4-120101.html

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

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多