一、前言
從入職到現在已有一年。想想現在與當初自己的期望雖有遺憾但也還是有所進步。我個人對自己的認知是敢於嘗試與實踐新的技術與新的理論,說大膽也不爲過。因此工作中寫的代碼或多或少也被詬病、被批評、被質疑。但我覺得若人人都循規蹈矩、人人都不去嘗試,那麼談何創新、談何進步呢?終究是需要人去做那一顆劃破靜夜的流星。
二、背景
最近在對項目的SDK進行升級改造。考慮到要兼容之前用戶的調用習慣,所以改造是需要很多講究的,也引發了對代碼架構的思考。在大多數時候我們在完成一個需求的時候,大部分人都是努力去完成它,即便給時間優化,我們往往也從代碼規範、性能上進行優化,似乎沒有人考慮可擴展性。當然這需要一定的前瞻性、也或許是因爲我們缺乏對該功能的全局意識不知道該功能未來會發生如何變化。
三、案例闡述
- 1,業務A-組合服務間的協作。
在該業務場景下,多線程處理且需要多個服務組合使用,有些服務需要依託其他服務的處理信息。一般我們的處理的思路是根據其他服務的返回值進行判斷再做處理。如果我們不做任何思考,就按照這個思路做下去,我覺得這樣子,代碼結構相對混亂,代碼可擴展性也很差。比如我現在給這個業務新加了一個服務,其他服務都需依賴我得服務處理信息進行處理,這就要對代碼結構進行調整了,代價高昂。示例僞代碼如下:
public class Business{ private ServiceA serviceA; private ServiceB serviceB; public void invoke(){ if(serviceA.doSomething()){ serviceB.doSomething(); } } } interface class ServiceA{ boolean doSomething(); } interface class ServiceB{ boolean doSomething(); }
現在我們思考是否有其他成熟的框架解決過類型的業務?我們知道Servlet對於多線程對當前應用的感知是通過Context完成的。受此啓發,這種涉及到多線程、多服務的組合調用我們可以給該業務場景綁定一個context,我們可以再任何服務通過獲取context信息,以確定當前服務是否可繼續執行。調整的代碼結構:
public class Business{ private ServiceA serviceA; private ServiceB serviceB; private Context context; public void invoke(){ serviceA.doSomething(); if(context.hasSomeServiceError()){ // end return ; } } } interface class ServiceA{ void doSomething(); } interface class ServiceB{ void doSomething(); } class context{ /* 是否有其他服務執行失敗 */ public boolean hasSomeServiceError(){} }
上面的實現,主要是解耦了服務間的強依賴調用使得程序可擴展性更好。
- 2,業務B-不同場景切換不同實現。
在該業務下,有多個場景,每個場景需要不同的邏輯。一般的思路是有多個場景,我們就定義多個實現,然後再一個服務方法中,通過不同的場景去選擇不同的實現。用僞代碼描述如下:
publis class Service{ public void invoke(type type){ switch(type){ // 場景爲aType case aType: // 獲取到aType的服務發起調用 getAtypeService().invoke(); break; } } }
但是這樣帶來的問題在於,你每次添加一個場景都需要去寫一個獲取該type對應服務的方法,並且到主方法裏面去發起對該場景type的調用。這樣子做是可以的,但是它增加了開發的複雜度。像這種頻繁的場景切換對應不同的實現,我們想一想是否有遇到過類似的需求再其他地方有實現我們可借鑑呢?這個貌似我沒有遇到過,但是我知道一個相似的理論:通道與管道。通道負責下達執行命令的指令,管道負責將命令送達到執行的地方。基於此我們可以將最外層的調用抽象層通道,裏面不同的實現抽象爲不同的管道。然後每個管道都有自己的標識,我們通過命令選擇對應的管道去執行。優化後的代碼結構:
public class ServiceChannel{ public void invoke(Type type){ // 發起調用 findPipeLine(type).invoke(); } private ServicePipeline findPipeLine(Type type){ // 找對應的管道,一般我們要求實現管道接口的類,都要注入到Spring上下文環境中 // 再這裏我們可通過spring上下文獲取到所有管道 } /** * 對於不同的場景我們只需要實現這個接口,注入到Spring上下文環境,無需其他任何操作,就可實現不同場景不同服務調用了 */ public static SerivcePipeline{ // 發起調用 void invoke(); // 管道類型 Tyep tyep(); } }
上面的實現主要將一些不必要的邏輯對用戶進行屏蔽,以及解耦實現與調用的關聯。
四、總結
歸根結底,一份好的代碼結構、必定是要考慮代碼隔離、解耦等多方面的。對於這方面確實有待加強、後續看一下有關這方面的理論知識還是很有必要的