最近給團隊新人講了一些設計上的常識,可能會對其它的新人也有些幫助, 把暫時想到的幾條,先記在這裏。數據庫
一、API與SPI分離編程
框架或組件一般有兩類客戶,一個是使用者,一個是擴展者。 API(Application Programming Interface)是給使用者用的, 而SPI(Service Provide Interface)是給擴展者用的。 在設計時,儘可能把它們隔離開,而不要混在一塊兒, 也就是說,使用者是看不到擴展者寫的實現的。json
好比:數組
SPI接口混在了API接口中,合理的方式是,有一個單獨的Renderer接口,有VelocityRenderer和JsonRenderer實現, Web框架將Action的輸出轉交給Renderer接口作渲染輸出。安全
反正例子:bash
正確例子:網絡
二、服務域/實體域/會話域分離架構
任何框架或組件,總會有核心領域模型,好比:app
實體域:像Spring的Bean,Struts的Action,Dubbo的Service,Napoli的Queue等等 。這個核心領域模型及其組成部分稱爲實體域,它表明着咱們要操做的目標自己, 實體域一般是線程安全的,無論是經過不變類,同步狀態,或複製的方式。框架
服務域:也就是行爲域,它是組件的功能集,同時也負責實體域和會話域的生命週期管理。好比Spring的ApplicationContext,Dubbo的ServiceManager等, 服務域的對象一般會比較重,並且是線程安全的,並以單一實例服務於全部調用。
會話域:就是一次交互過程, 會話中重要的概念是上下文,什麼是上下文? 好比咱們說:「老地方見」,這裏的「老地方」就是上下文信息, 爲何說「老地方」對方會知道,由於咱們前面定義了「老地方」的具體內容, 因此說,上下文一般持有交互過程當中的狀態變量等, 會話對象一般較輕,每次請求都從新建立實例,請求結束後銷燬。
簡而言之: 把元信息交由實體域持有, 把一次請求中的臨時狀態由會話域持有, 由服務域貫穿整個過程。
實例一
實例二
兩 種 例 子
複製代碼
三、在重要的過程上設置攔截接口
1.若是你要寫個遠程調用框架,那遠程調用的過程應該有一個統一的攔截接口;
2.若是你要寫一個ORM框架,那至少SQL的執行過程,Mapping過程要有攔截接口;
3.若是你要寫一個Web框架,那請求的執行過程應該要有攔截接口;
複製代碼
等等,就能夠自行完成,而不用侵入框架內部。攔截接口,一般是把過程自己用一個對象封裝起來,傳給攔截器鏈。
好比:遠程調用主過程爲invoke(),那攔截器接口一般爲invoke(Invocation),Invocation對象封裝了原本要執行過程的上下文,而且Invocation裏有一個invoke()方法, 由攔截器決定何時執行。同時,Invocation也表明攔截器行爲自己, 這樣上一攔截器的Invocation實際上是包裝的下一攔截器的過程, 直到最後一個攔截器的Invocation是包裝的最終的invoke()過程, 同理,SQL主過程爲execute(),那攔截器接口一般爲execute(Execution),原理同樣, 固然,實現方式能夠任意,上面只是舉例。
四、重要的狀態的變動發送事件並留出監聽接口
這裏先要講一個事件和上面攔截器的區別:
攔截器:是干預過程的,它是過程的一部分,是基於過程行爲的。 事件:是基於狀態數據的,任何行爲改變的相同狀態,對事件應該是一致的,事件一般是過後通知,是一個Callback接口,方法名一般是過去式的,好比onChanged()。
好比遠程調用框架,當網絡斷開或連上應該發出一個事件,當出現錯誤也能夠考慮發出一個事件, 這樣外圍應用就有可能觀察到框架內部的變化,作相應適應。
五、擴展接口職責儘量單一,具備可組合性
好比,遠程調用框架它的協議是能夠替換的, 若是隻提供一個總的擴展接口,固然能夠作到切換協議, 但協議支持是能夠細分爲底層通信,序列化,動態代理方式等等, 若是將接口拆細,正交分解,會更便於擴展者複用已有邏輯,而只是替換某部分實現策略, 固然這個分解的粒度須要把握好。
六、微核插件式,平等對待第三方
大凡發展的比較好的框架,都遵照微核的理念
Eclipse的微核是OSGi, Spring的微核是BeanFactory,Maven的微核是Plexus。
一般核心是不該該帶有功能性的,而是一個生命週期和集成容器, 這樣各功能能夠經過相同的方式交互及擴展,而且任何功能均可以被替換, 若是作不到微核,至少要平等對待第三方, 即原做者能實現的功能,擴展者應該能夠經過擴展的方式所有作到, 原做者要把本身也看成擴展者,這樣才能保證框架的可持續性及由內向外的穩定性。
七、不要控制外部對象的生命週期
好比上面說的Action使用接口和Renderer擴展接口, 框架若是讓使用者或擴展者把Action或Renderer實現類的類名或類元信息報上來。而後在內部經過反射newInstance()建立一個實例, 這樣框架就控制了Action或Renderer實現類的生命週期, Action或Renderer的生老病死,框架都本身作了,外部擴展或集成都無能爲力。
好的辦法是讓使用者或擴展者把Action或Renderer實現類的實例報上來, 框架只是使用這些實例,這些對象是怎麼建立的,怎麼銷燬的,都和框架無關, 框架最多提供工具類輔助管理,而不是絕對控制。
八、可配置必定可編程,並保持友好的CoC約定
由於使用環境的不肯定因素不少,框架總會有一些配置, 通常都會到classpath直掃某個指定名稱的配置,或者啓動時容許指定配置路徑, 作爲一個通用框架,應該作到凡是能配置文件作的必定要能經過編程方式進行, 不然當使用者須要將你的框架與另外一個框架集成時就會帶來不少沒必要要的麻煩。
另外,儘量作一個標準約定,若是用戶按某種約定作事時,就不須要該配置項。 好比:配置模板位置,你能夠約定,若是放在templates目錄下就不用配了, 若是你想換個目錄,就配置下。
九、區分命令與查詢,明確前置條件與後置條件
這個是契約式設計的一部分,儘可能遵照有返回值的方法是查詢方法,void返回的方法是命令, 查詢方法一般是冪等性的,無反作用的,也就是不改變任何狀態,調n次結果都是同樣的。好比get某個屬性值,或查詢一條數據庫記錄。
命令是指有反作用的,也就是會修改狀態,好比set某個值,或update某條數據庫記錄, 若是你的方法即作了修改狀態的操做,又作了查詢返回,若是可能,將其拆成寫讀分離的兩個方法。
好比:
十、增量式擴展,而不要擴充原始核心概念
咱們平臺的產品愈來愈多,產品的功能也愈來愈多, 平臺的產品爲了適應各BU和部門以及產品線的需求。勢必會將不少不相干的功能湊在一塊兒,客戶能夠選擇性的使用, 爲了兼容更多的需求,每一個產品,每一個框架,都在不停的擴展, 而咱們常常會選擇一些擴展的擴展方式,也就是將新舊功能擴展成一個通用實現。
我想討論是,有些狀況下也能夠考慮增量式的擴展方式,也就是保留原功能的簡單性,新功能獨立實現。我最近一直作分佈式服務框架的開發,就拿咱們項目中的問題開涮吧。
好比:遠程調用框架,確定少不了序列化功能,功能很簡單,就是把流轉成對象,對象轉成流, 但因有些地方可能會使用osgi,這樣序列化時,IO所在的ClassLoader可能和業務方的ClassLoader是隔離的, 須要將流轉換成byte[]數組,而後傳給業務方的ClassLoader進行序列化。
爲了適應osgi需求,把原來非osgi與osgi的場景擴展了一下, 這樣,無論是否是osgi環境,都先將流轉成byte[]數組,拷貝一次。然而,大部分場景都用不上osgi,卻爲osgi付出了代價, 而若是採用增量式擴展方式,非osgi的代碼原封不動, 再加一個osgi的實現,要用osgi的時候,直接依賴osgi實現便可。
再好比:最開始,遠程服務都是基於接口方法,進行透明化調用的, 這樣,擴展接口就是,invoke(Method method, Object[] args), 後來,有了無接口調用的需求,就是沒有接口方法也能調用,並將POJO對象都轉換成Map表示, 由於Method對象是不能直接new出來的,咱們不自覺選了一個擴展式擴展, 把擴展接口改爲了invoke(String methodName, String[] parameterTypes, String returnTypes, Object[] args), 致使無論是否是無接口調用,都得把parameterTypes從Class[]轉成String[]。
若是選用增量式擴展,應該是保持原有接口不變, 增長一個GeneralService接口,裏面有一個通用的invoke()方法, 和其它正常業務上的接口同樣的調用方式,擴展接口也不用變, 只是GeneralServiceImpl的invoke()實現會將收到的調用轉給目標接口, 這樣就能將新功能增量到舊功能上,並保持原來結構的簡單性。
再再好比:無狀態消息發送,很簡單,序列化一個對象發過去就行, 後來有了同步消息發送需求,須要一個Request/Response進行配對, 採用擴展式擴展,天然想到,無狀態消息實際上是一個沒有Response的Request, 因此在Request里加一個boolean狀態,表示要不要返回Response, 若是再來一個會話消息發送需求,那就再加一個Session交互。而後發現,原來同步消息發送是會話消息的一種特殊狀況, 全部場景都傳Session,不須要Session的地方無視便可。 若是採用增量式擴展,無狀態消息發送原封不動。
同步消息發送,在無狀態消息基礎上加一個Request/Response處理, 會話消息發送,再加一個SessionRequest/SessionResponse處理。
爲何某些人會一直比你優秀,是由於他自己就很優秀還一直在持續努力變得更優秀,而你是否是還在知足於現狀心裏在竊喜!
我本人邀約各大BATJ架構大牛共創Java架構師社區羣,(羣號:673043639)致力於免費提供Java架構行業交流平臺,經過這個平臺讓你們相互學習成長,提升技術,讓本身的水平進階一個檔次,成功通往Java架構技術大牛或架構師發展
合理利用本身每一分每一秒的時間來學習提高本身,不要再用"沒有時間「來掩飾本身思想上的懶惰!趁年輕,使勁拼,給將來的本身一個交代!
To-陌霖Java架構
複製代碼
分享互聯網最新文章 關注互聯網最新發展