java 示例_功能Java示例 第2部分–讲故事
java 示例
這是稱為“ Functional Java by Example”的系列文章的第2部分。
我在本系列的每個(gè)部分中開發(fā)的示例是某種“提要處理程序”,用于處理文檔。 在上一部分中,我從一些原始代碼開始,并應(yīng)用了一些重構(gòu)來描述“什么”而不是“如何”。
為了幫助代碼向前發(fā)展,我們需要先講一個(gè)故事 。 這就是這個(gè)部分的來歷。
如果您是第一次來這里,最好從頭開始閱讀。 它有助于了解我們從何處開始以及如何在整個(gè)系列中繼續(xù)前進(jìn)。
這些都是這些部分:
- 第1部分–從命令式到聲明式
- 第2部分–講故事
- 第3部分–不要使用異常來控制流程
- 第4部分–首選不變性
- 第5部分–將I / O移到外部
- 第6部分–用作參數(shù)
- 第7部分–將失敗也視為數(shù)據(jù)
- 第8部分–更多純函數(shù)
我將在每篇文章發(fā)表時(shí)更新鏈接。 如果您通過內(nèi)容聯(lián)合組織來閱讀本文,請(qǐng)查看我博客上的原始文章。
每次代碼也被推送到這個(gè)GitHub項(xiàng)目 。
作為參考,我們現(xiàn)在以以下代碼為起點(diǎn):
class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {changes.findAll { doc -> doc.type == 'important' }.each { doc ->try {def resource = webservice.create(doc)doc.apiId = resource.iddoc.status = 'processed'} catch (e) {doc.status = 'failed'doc.error = e.message}documentDb.update(doc)}} }大聲讀出
當(dāng)我最初開始使用Spock作為測試框架時(shí),由于它是多年前Grails默認(rèn)提供的,因此它的眾多功能和易用性給我留下了深刻的印象(至今仍是)。
你知道什么是假人,存根和間諜吧? Mockito擁有它們,Powermock擁有它們以及基本上所有其他嚴(yán)肅的(單元)測試框架。 Mock的概念并不難掌握(您可以在此處閱讀全部內(nèi)容),但是Spock具有一種特殊的方式來描述其(與預(yù)期的)模擬交互。
關(guān)于“基于交互的測試”的一章非常出色,它解釋了如何使用代碼示例來記錄這些交互。
“模擬”子章開頭為:
模擬是描述(強(qiáng)制)規(guī)范下的對(duì)象與其協(xié)作者之間的交互的行為。 這是一個(gè)例子:
def "should send messages to all subscribers"() {when:publisher.send("hello")then:1 * subscriber.receive("hello")1 * subscriber2.receive("hello") }如果您不熟悉Spock,Groovy或僅僅具有上述寫作風(fēng)格,請(qǐng)不要擔(dān)心!
Spock上面的文檔的作者也認(rèn)識(shí)到,并不是每個(gè)人都會(huì)立即理解這里發(fā)生的事情。
他們會(huì)提供一些建議,并繼續(xù)提供文檔:
大聲讀出 :“當(dāng)發(fā)布者發(fā)送“ hello”消息時(shí),兩個(gè)訂戶都應(yīng)該只收到一次該消息?!?
我對(duì)“大聲朗讀”的重視是我的,因?yàn)槲艺J(rèn)為這很重要。 這里不討論Spock的更多細(xì)節(jié),而是我自己在日常編碼中要牢記的建議本身。
- 當(dāng)我編寫一段代碼時(shí),我可以大聲讀出嗎?
- 當(dāng)其他人讀取我的代碼時(shí),他/她可以大聲讀出嗎?
這里的“大聲” 與音量無關(guān),而是可以用一種簡潔易懂的方式描述“這里正在發(fā)生什么”。 這使得對(duì)代碼的推理變得容易。
高級(jí)別與低級(jí)別
請(qǐng)考慮以下情形:
在不知名的城市中開車游覽了幾個(gè)小時(shí)以找到劇院后,汽車導(dǎo)航系統(tǒng)出現(xiàn)故障后,您最終決定停下來詢問方向。
您在行人附近停下。
您:
先生,您碰巧知道如何從這里到達(dá)劇院
行人:
當(dāng)然,這很容易。 開始了:
檢查窗戶以確保您具有良好的可見性。 檢查后視鏡以確保它們正確對(duì)齊,從而為您提供正確的道路視野。
調(diào)整座椅,使雙腳舒適地到達(dá)兩個(gè)踏板。
關(guān)閉視窗。
重置轉(zhuǎn)向信號(hào)燈。
開始駕駛前,請(qǐng)松開駐車制動(dòng)器。
啊,我看你有自動(dòng)檔。 請(qǐng)把變速桿放在“驅(qū)動(dòng)器”中。
慢慢踩剎車,并監(jiān)控儀表盤儀表。
繼續(xù)前進(jìn),提高速度,監(jiān)控車速表,將RPM保持在2000附近
大約120碼后,在開始轉(zhuǎn)向左車道之前,請(qǐng)先與您的方向燈指示至少兩秒鐘。
緩慢將汽車移至另一車道。 稍微將您的手放在方向盤上,以改變車道。 車輪只需要很小的移動(dòng)即可; 因?yàn)榇蠖鄶?shù)現(xiàn)代汽車都裝有動(dòng)力轉(zhuǎn)向系統(tǒng)。 更改車道大約需要一到三秒鐘。 減少一點(diǎn),您做得太快了; 再也沒有,你做得太慢了。
再走X步…
祝好運(yùn)!
或者,考慮對(duì)話將像這樣的替代宇宙:
您:
先生,您是否會(huì)知道如何從這里到達(dá)劇院?
行人:
當(dāng)然,這很容易。 開始了:
左轉(zhuǎn),過橋。 在你的右邊。
祝好運(yùn)!
最后一種情況是輕而易舉:明確指示要做什么和去哪里!
但是,第一種情況是細(xì)節(jié)纏身 -有關(guān)駕駛汽車本身的低級(jí)細(xì)節(jié) -即使我們不希望在現(xiàn)實(shí)生活中得到這樣的指導(dǎo),我們?nèi)匀粫?huì)編寫這樣的軟件。
告訴我一些正確的內(nèi)容。 如果我需要具體信息,我會(huì)要求它。
(順便說一句wikihow.com:如何駕駛汽車,請(qǐng)捐贈(zèng)上述說明中的一些。如果您確實(shí)需要學(xué)習(xí)駕駛,它有很多資源!)
在正確的級(jí)別上講內(nèi)容,不僅意味著使用正確命名的類和方法,而且還意味著在其中使用正確的抽象類型 。
讓我們?cè)俅慰匆幌挛覀兊拇a:
class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {changes.findAll { doc -> doc.type == 'important' }.each { doc ->try {def resource = webservice.create(doc)doc.apiId = resource.iddoc.status = 'processed'} catch (e) {doc.status = 'failed'doc.error = e.message}documentDb.update(doc)}} }故事
我們?nèi)绾卧诖a中結(jié)合“大聲讀出”和“高級(jí)還是低級(jí)”?
我們的單handle方法當(dāng)前顯示為什么樣?
查找type -property等于字符串"important"所有文檔。
呼叫create的webservice與文檔,返回的資源。
如果我們有資源,請(qǐng)將資源的id分配給文檔的apiId屬性。
將文檔的status屬性設(shè)置為字符串"processed" 。
如果發(fā)生異常,請(qǐng)將文檔的status屬性設(shè)置為字符串"failed" 。 將文檔的status屬性設(shè)置為來自異常的message 。
最后,使用documentDb在documentDb上調(diào)用update 。
基本上,這只是重復(fù)代碼語句!
什么故事,我想告訴取而代之的,是以下情況:
通過Web服務(wù)“創(chuàng)建資源”來處理“重要”文檔。
每次成功時(shí),將兩者關(guān)聯(lián)在一起并“將文檔標(biāo)記為已處理”,否則將其標(biāo)記為“失敗”。
讀得很好,你不覺得嗎?
實(shí)際上,我們可以通過在IDE中使用幾種“提取方法”重構(gòu)并為提取的方法選擇一些好的名稱來實(shí)現(xiàn)這一目標(biāo)。
上面故事中用雙引號(hào)引起的短語是我想在高層看到的重要部分。
“重要”
我為什么要關(guān)心文檔使用什么屬性來確定其重要性? 現(xiàn)在是字符串"important" ,它表示“嘿,我很重要!” 但是如果條件變得更加復(fù)雜怎么辦?
將doc.type == 'important'提取到其自身的方法isImportant 。
changes.findAll { doc -> isImportant(doc) }// ...private boolean isImportant(doc) {doc.type == 'important'}“創(chuàng)造資源”
為什么我在這里關(guān)心如何在Web服務(wù)中調(diào)用什么方法? 我只想創(chuàng)建一個(gè)資源。
將與Web服務(wù)的所有處理都提取到它自己的方法(稱為createResource 。
def resource = createResource(doc)// ...private Resource createResource(doc) {webservice.create(doc)}“更新為已處理”
提取將資源/文檔/將狀態(tài)設(shè)置為其自己的方法(稱為updateToProcessed 。
updateToProcessed(doc, resource)// ...private void updateToProcessed(doc, resource) {doc.apiId = resource.iddoc.status = 'processed'}“更新失敗”
不在乎細(xì)節(jié)。 提取到updateToFailed 。
updateToFailed(doc, e)// ...private void updateToFailed(doc, e) {doc.status = 'failed'doc.error = e.message}似乎我們最后只剩下了documentDb.update(doc) 。
這是在數(shù)據(jù)庫中存儲(chǔ)已處理/失敗文檔的一部分,我已經(jīng)在最高級(jí)別上進(jìn)行了描述。
我將其放在每個(gè)剛剛創(chuàng)建的updateTo*方法中-一個(gè)較低的級(jí)別。
private void updateToProcessed(doc, resource) {doc.apiId = resource.iddoc.status = 'processed'documentDb.update(doc)}private void updateToFailed(doc, e) {doc.status = 'failed'doc.error = e.messagedocumentDb.update(doc)}那么,提取出細(xì)節(jié)之后,有什么變化?
void handle(List<Doc> changes) {changes.findAll { doc -> isImportant(doc) }.each { doc ->try {def resource = createResource(doc)updateToProcessed(doc, resource)} catch (e) {updateToFailed(doc, e)}}}任何人(例如,同事,您未來的自我)都會(huì)“一口氣”地讀一讀,將了解30,000英尺的行程。
如果您需要任何這些步驟的詳細(xì)信息,只需深入了解該方法即可。
能夠?qū)懧暶魇降臇|西(本系列的前一部分)并在正確的水平上講故事(本部分)還將有助于在第3部分及以后的部分中更輕松地進(jìn)行將來的更改。
現(xiàn)在就這樣
作為參考,這是重構(gòu)代碼的完整版本。
class FeedHandler {Webservice webserviceDocumentDb documentDbvoid handle(List<Doc> changes) {changes.findAll { doc -> isImportant(doc) }.each { doc ->try {def resource = createResource(doc)updateToProcessed(doc, resource)} catch (e) {updateToFailed(doc, e)}}}private Resource createResource(doc) {webservice.create(doc)}private boolean isImportant(doc) {doc.type == 'important'}private void updateToProcessed(doc, resource) {doc.apiId = resource.iddoc.status = 'processed'documentDb.update(doc)}private void updateToFailed(doc, e) {doc.status = 'failed'doc.error = e.messagedocumentDb.update(doc)}}翻譯自: https://www.javacodegeeks.com/2017/11/functional-java-example-part-2-tell-story.html
java 示例
總結(jié)
以上是生活随笔為你收集整理的java 示例_功能Java示例 第2部分–讲故事的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我国首条空轨在武汉开通:全自动无人驾驶、
- 下一篇: java美元兑换,(Java实现) 美元