天天看點

為什麼又要造一個叫 Latke 的輪子

使用架構的好處很多,它規範了我們的開發方式,減少了出錯的可能性,讓我們可以更快地完成開發目标,後續維護也可以有章可循;使用架構的弊端也很明顯,它束縛了我們,離開熟悉的架構進行開發我們可能會手足無措,它讓我們身陷其中。但無論如何,我們是離不開架構的,多認識幾種架構是沒錯的,java web 領域更是如此(選擇很多,同時也很少)。

到目前為止,我所認識的架構無一不例外都是以 class 作為實體類型的,為什麼會這樣?為什麼不能以其他形式(例如 map)作為實體載體?我覺得這些問題很值得讨論(雖然以前可能已經讨論過無數次)、很值得進行實踐。

為了把這個“想法”表達清楚,我們先看看一直以來在應用開發領域熱議的一些話題,最後再看看“想法”結晶——latke 這個輪子是否能跑。

在讨論程式設計語言的時候,我們經常會聽到如“xxx語言不是類型安全的”,“xxx是動态語言,程式設計時檢查不了類型錯誤”等等此類。在過去(10 多年以前吧)這可能真的是一個“缺陷”, 但在今天來看,弱類型動态語言在應用開發領域(不是系統開發領域)越來越受歡迎,并且其他一些程式設計語言也在做類似的文法糖。

我想最大的原因就是弱類型語言在代碼修改時更快捷、成本更低,盡管我們現在使用的 ide 重構輔助能力很強,可一旦實體模型發生字段變化,相關的修改也是夠頭疼的(特别是應用間互動的 dto,修改成本瞬間飙升)。

當然,弱類型的缺點也是顯而易見的,就是除了開發者本人,其他人很難搞清楚(隻看代碼片段)這個變量到底是什麼,到底包含了什麼字段,要徹底搞清楚隻能通過通讀相關程式代碼 (如果有準确的文檔就友善多了)。無論如何,現如今很多應用開發都是選擇弱類型語言,并且已經得到了廣泛運維驗證(php、node.js)。

近些年,json 無疑已經成為了資料載體的一個标準,幾乎所有程式設計語言、開發架構、存儲系統都支援 json 格式,最重要的是 json 是在浏覽器端預設支援的結構化資料的方式。

在伺服器端,使用 json 的地方(或者說和 json 相關的開發)也越來越多,pojo(實體對象/entity)和 json 互相轉換無時不在發生:前端送出請求,參數是 json 格式,控制器接到請求後将 json 實參轉為 java pojo,操作這個對象、生成響應(可能也是一個 json),最終傳回前端,完成這次請求處理。在這個過程中,至少包含了兩次 json 和 pojo 的互相轉換,雖然有很多工具(例如 jackson)能夠幫助我們完成 json-pojo 映射,但是這樣做的副作用也很明顯:需要再學習一個工具(要能夠正确使用它);額外的性能開銷(有時很小,但有時很巨大);代碼看上去怪怪的(不自然,工程化的“模型變換”)。

json 的确是好(簡單有效,沒有過度設計),但為什麼不能從前到後的使用 json 呢?

将 pojo 持久化到關系型資料庫的過程就是 orm。但因為存在阻抗不比對的問題,是以再優秀的 orm 方案也是存在問題的(性能問題、複雜查詢問題),在解決這類問題的時候,通常做法都是直接寫 sql。從 orm 實際實作上看,xbatis 的思路比 jpa 系更正确一些,但同時也略顯繁瑣了一些(需要定義 mapper.xml)。

資料庫表是二維的,資料總是可以轉為鍵值對集合/map 的(jdbc 結果集接口就是這樣幹的),反之亦然。一個查詢 sql 傳回的結果集可以很容易就轉換為 map,複雜的是将這個 map 轉換為 pojo(嵌套的實體必須根據嵌套元資訊才能完成映射)。

言至此,我們肯定會問一個問題:為什麼不能直接使用 map 呢?

前些年,“領域模組化”這個詞非常流行,任何設計方案都要帶上這頂帽子才好意思和别人打招呼。那些年,要解決“使用者登入”都要精心模組化:

“user 類必須有。”

“嗯,最好抽象出個 iuser 接口。”

“既然都有接口了,再整個抽象基類吧。”

“嗯,很專業,成體系。”

“呃,等等,login 接口放 iuser 裡吧?還是放 userservice 裡?”

“放 service 裡,大家都這麼幹的,放 user 裡 spring 好像不支援吧。”

最終,一個完美的 java 航母應用開發完了,它确實能夠航行,但是要換個螺絲或者加個床位的時候:“等我找下設計圖,嗯,換螺絲要拆掉 a~z 這幾個地方就能看見要換的螺絲了,加床位可能不行,哦,嬰兒床應該可以”。

這一切大部分都歸咎于一開始我們讨論的:類型。類型一旦固定,就真的固定了,無論我們設計的抽象體系有多圓,最終都無法做到無損擴充(不可能真正達到開閉原則),因為所有的精密抽象都是存在洩漏的,面向類類型的程式設計範式在解決問題時不夠直接,并且很難修改。

把上面讨論的内容揉在一起,揉着揉着,居然變成了一個輪子——latke(項目托管在 github 上,歡迎 star/fork)。

前後端分離

類似 tapestry、wicket、jsf、gwt 的思路都是反前端的,前端該是什麼樣就是什麼樣(html/js/css),當然,伺服器端的模闆引擎還是需要的(比如 freemarker)。最終前端選擇什麼架構、工具絕對是前端開發決定,和後端沒什麼關系。

隻有 json

請求實參 json 對象(很少情況是其他格式)傳到控制器後,不用轉為 pojo(因為我們壓根沒這個),直接操作這個 json(修改字段值、增減字段),并且可以很容易就将它持久化到資料庫中了。大多數時候都不用寫 sql,少數時候就擷取資料庫連接配接,jdbc 吧。

有 schema

雖然從前到後都是使用 json,但也不用擔心資料結構混亂,因為表結構和 json 的映射是有配置檔案定義的,可以通過這個結構定義生成建表 sql,也可以通過已有的資料庫表生成這個結構定義。

基于 servlet

另起爐竈(比如 play)無論是對做輪子還是對用輪子的人來說成本都太高了,而且 servlet 對 http 的抽象還是比較适當的,這個真沒必要再弄一套,latke 輪子實在碾不過去的地方就直接操作 request 和 response 吧,這個大家都會。

多種 db

支援多種資料庫的 json 化 crud,要更換資料庫(雖然真實世界很少發生)很友善:有資料導出/導入工具,無痛資料遷移(比如從 gae datastore 遷移到 mysql)。

插件

可以在不改動任何一行現有代碼的前提下添加新功能,而且這個新功能是完整的(前端後端都有),可以很容易就內建到現有界面中的任何地方。

各種工具

cache、event、cron、ioc、i18n、http client、mail、themes 已經内置,雖不敢說每個服務功能如何強大,但我敢說對大部分的應用場景已經足夠使了,并且輪子本身的第三方依賴也是精挑細選的,這些工具加一起真的足夠了。

性能

從實作上看是 servlet 的薄封裝,理論上和直接使用 servlet 性能差距不會太大,實際上我們也是進行過壓測的,結果顯示沒有性能問題。

再進一步

“用上 latke 輪子後開發效率提升一大截,但還是嫌前進太慢,怎麼破?”

這已經不是輪子圓不圓的問題了,這是造輪子時使用的材料問題(風火輪和我們汽車輪子一樣圓吧,可實際上速度不是一個量級的,還可以當作武器,另外,這個還和使用的人有關吧)。

java 就這樣了,我們既然用了就說明我們已經接受它了,既然接受了就得忍讓着點,最終忍不了就可以像王垠大神一樣。不過對于我們凡人來說,比較切實可行的做法是換個程式設計語言,種種迹象表明,node.js 在應用開發領域已經風生水起。

最後

前面說了一大推來證明 latke 是圓的,要是你不相信,請看下《latke 快速上手指南》,裡面有個 demo;要是你還不相信,就親自試用吧 ;-p