溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何從Moco解析程序庫設(shè)計(jì)

發(fā)布時(shí)間:2021-10-29 17:23:33 來源:億速云 閱讀:171 作者:柒染 欄目:編程語言

如何從Moco解析程序庫設(shè)計(jì),相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個(gè)問題。

Moco在程序庫設(shè)計(jì)包括兩個(gè)方面,如何設(shè)置服務(wù)器和如何讓服務(wù)器運(yùn)行起來。

先說簡單的,如何讓服務(wù)器運(yùn)行。最簡單的選擇是讓用戶自己啟動服務(wù)器,然后,在測試結(jié)束之后關(guān)閉服務(wù)器。這么一來,每個(gè)測試都大概會是這樣:

public void shouldWork() {    ... setup server ...    try {      server.start();      ... your test here ...     } finally {      server.stop();    }  }

作為一個(gè)框架,留給客戶使用的接口一定簡單,把實(shí)現(xiàn)細(xì)節(jié)封裝在框架以內(nèi)。如果每個(gè)測試都這么寫,用戶很快就會罵娘了。現(xiàn)在的Moco的做法是使用了running方法,把Server啟停的細(xì)節(jié)封裝了起來。

running(server, new Runnable() {    @Override   public void run() throws Exception {      assertThat(helper.get(root()), is("foo"));    }  });

我知道你不喜歡匿名內(nèi)部類,我也不喜歡,但是,這是Java的局限。等到Java 8駕臨,相信一切會有所改觀。

設(shè)置服務(wù)器,也就是在什么樣的請求,給什么樣的應(yīng)答。Moco有一個(gè)很關(guān)鍵的起點(diǎn),那便是DSL,要知道,之前的一段時(shí)間我剛剛完成了《領(lǐng)域特定語言》的翻譯。于是,API的可讀性成了一件非常重要的事。

說起來很簡單,但是,要知道匹配請求的條件有很多,而且還可能任意組合。如果這些條件是彼此獨(dú)立的(“或”),我可以用變參來解決,但有時(shí),這些條件是相關(guān)的(“與”),那該怎么辦呢?拜函數(shù)式編程思路所賜,我想到了函數(shù)組合的方式。

我知道,Java沒有一等公民的函數(shù),但是,Java里有一等公民的對象,我們可以用對象模擬函數(shù),事實(shí)上,這也是很多面向?qū)ο蟪绦蛟O(shè)計(jì)語言解決函數(shù)組合的一種方式,而這種對象稱為函數(shù)對象,也叫functor。

把需要的條件封裝成一個(gè)個(gè)函數(shù)對象,然后通過幾個(gè)簡單的運(yùn)算符就可以將它們組合起來。目前Moco里提供了and和or兩個(gè)運(yùn)算符用于組合。

server.request(and(by(uri("/foo")), by(method("put")))).response("bar");  server.request(or(by("foo"), by(uri("/foo")))).response("bar");

隨著面向?qū)ο笏悸返钠占埃瘮?shù)組合的寫法會越來越普遍的,它會成為程序員工具箱中的必備品。

DSL是一個(gè)重要的考量,所以,這里沒有直接new對象,而是用函數(shù)(比如uri、method)做了一層封裝。或許你會說,那為了可讀性,我可以把類名設(shè)計(jì)成一個(gè)可讀的樣子,但new一個(gè)對象的問題在哪呢?就在new上。一方面,new是為了創(chuàng)建一個(gè)對象,這是實(shí)現(xiàn)細(xì)節(jié),與我們要表達(dá)的內(nèi)容不在一個(gè)層次,另一方面,你會發(fā)現(xiàn)多個(gè)new會讓這句話顯得不連貫。這句話?是的,我們的寫法就像是用一句話聲明一個(gè)東西一樣,而我們期望自己講的內(nèi)容有一定的連貫性,而這才是DSL。

一旦設(shè)計(jì)成DSL,單個(gè)函數(shù)的文檔也就失去了意義,更重要的是一個(gè)用法的文檔。所以,在Moco里,我寫了文檔,卻沒有寫JavaDoc。

在Moco里,請求和應(yīng)答里可能有同樣的東西,比如file,而request和response的參數(shù)類型是不一樣的。如果file能夠返回不同類型是***的,可惜在Java里面,我們不可能根據(jù)返回類型做重載。一種解決方案是為request和response分別設(shè)計(jì)一個(gè)函數(shù),比如requestFile和responseFile,但顯然,這種做法會把同樣的邏輯分散到兩個(gè)類實(shí)現(xiàn)里,而且這樣需要共享的東西不只是file,還有其它一些東西。

在計(jì)算機(jī)問題里,加上一個(gè)中間層永遠(yuǎn)是一個(gè)重要的解決方案。這也是by的目的所在。這樣一來,這些共享的東西就可以做成一個(gè)抽象(Resource),對請求來說,只要有一個(gè)by,就可以適配成Matcher。

在Moco設(shè)計(jì)中,還涉及到了一個(gè)框架設(shè)計(jì)中很重要的點(diǎn):類型。Java是靜態(tài)類型程序設(shè)計(jì)語言,利用好類型,就可以在編譯期把錯(cuò)誤報(bào)出來,而不會留到運(yùn)行時(shí),fail fast是很重要的一個(gè)程序設(shè)計(jì)實(shí)踐。

Moco目前支持一些Content Type檢測。如果把這個(gè)Content Type放到Resource里面,你會發(fā)現(xiàn)不是所有Resource都需要這個(gè),比如method,我們當(dāng)然可以在Resource接口里支持Content Type,在不需要的地方拋出異常。

在Moco里,我的做法是引入了一個(gè)ContentResource,支持Content Type,它繼承于Resource,這樣依賴,需要Content Type的接口(比如content),直接支持ContentResource,而其它部分繼續(xù)支持Resource,這樣,如果一不小心用錯(cuò)的話,編譯就會報(bào)錯(cuò)。

再有一點(diǎn)是關(guān)于Publish接口,在Moco的實(shí)現(xiàn)里有一個(gè)Setting,還有一個(gè)BaseSetting,如果觀察實(shí)現(xiàn),在設(shè)置Request會創(chuàng)建出一個(gè)BaseSetting,但其返回的接口是Setting。這么做的一個(gè)原因是,因?yàn)镾etting出現(xiàn)在用戶可以使用的接口上,而BaseSetting是留給內(nèi)部實(shí)現(xiàn)的,它提供了一些用于內(nèi)部實(shí)現(xiàn)的方法,而用戶是不應(yīng)該使用這些方法的,所以,將二者做了一個(gè)分離,這樣一來,保證了用戶不會誤操作。同樣處理的還有HttpServer和ActualHttpServer。

小結(jié)一番,簡化用戶接口,設(shè)計(jì)DSL,利用好類型,區(qū)分Publish接口。

看完上述內(nèi)容,你們掌握如何從Moco解析程序庫設(shè)計(jì)的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注億速云行業(yè)資訊頻道,感謝各位的閱讀!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI