軟體設計之不跟陌生人打交道

同人曾為一個軟體專案設計及建構服務導向架構。為了在軟體開發過程中,便於開發及測試,所以在架構上的設計上,在前端程式呼叫 Services 方面,是透過組態設定來決定直接連接後端的應用系統或間接透過如 Web Services遠端程序呼叫調用後端應用系統前端程式直接連接後端應用系統是為了開發階段測試方便,而正式上線後為了軟體配置考量則需要透過間接遠端程序呼叫調用後端應用系統。,透過組態設定的好處是不會因為軟體運作環境的不同配置而更動程式,並且可以巧妙地隔開各元件的相互耦合而達到獨立測試的好處,開發者也可以更專注在其專業領域上來開發系統。

這樣的架構可以做到為維護而設計,可是,這樣設計不可避免地會面臨到一個問題:物件要怎麼做到遠端傳送呢?對於每個領域物件,我們不希望另外寫 Value Objects/DTOs,我們希望用簡單樸實的 POJOs,有關領域物件的傳送,一個可能的做法便是:先將領域物件序列化Xml 格式,再傳送到遠端的目的地後再將 Xml 格式反序列化成領域物件。依據這樣的設計思路,我們找到了 XStream 這個 framework,它實作了各種 Java 物件的 Converters,可以把任何 Java 物件序列化成 Xml 格式及將 Xml 格式反序列化回 Java 物件,而且,它的 Xml 物件格式非常的簡單且直覺,對於複雜的物件,它甚至還提供自訂 Converter 的機制讓開發者能自定處理複雜物件的序列化/反序列化。

我遇到過最複雜的領域物件,使得 XStream 無法以預設的 Converters 來序列化/反序列化,應該就非 POIHSSFWorkbook 莫屬了,那是一個 Excel 的 workbook 物件,我的解法是利用 XStream 的自訂 Converters 機制,在序列化時將 HSSFWorkbook 物件轉成檔案串列流,反序列化則將檔案串列流轉成 Excel 的 workbook 物件。

不過,最近同事跑來找我求助,他發現在配置軟體系統的時候有錯誤發生,應用程式伺服器日誌顯示有物件無法從前端傳回來。從發生問題的程式片斷來研判,我猜測大概是 XStream 反序列化某個物件的時候出問題,為了確認問題所在,我寫了測試程式來確認它,結果不出所料,看起來那個物件太複雜了,應該要為它再寫 Converter 嗎?

慢著,這樣做好像不是有效的解決問題之道。因為,那個物件明明是只有後端程式才會用到的控制程式,為什麼需要傳到前端呢?程式這樣寫,其實是違反了 GRASP 的設計理念-不跟陌生人打交道的設計原則參考趙光正譯(2002),《UML與樣式徹底研究》,臺灣培生教育出版股份有限公司。

將責任委派給前端程式的直接對象,讓這個直接對象與後端的間接對象協同運作,這樣前端程式就無需知道後端的間接對象的存在。

為什麼我這麼說呢?先看下面這張圖:

圖中的前端程式就是 Client 這個元件,它連結了 BizService 這個界面,於是必須委派責任給實現這個界面的元件-BizServiceImpl,讓它負責與實現 ServiceSupport 界面的元件-ServiceSupportImpl 協同運作,所以照理說 Client 不需要知道有 ServiceSupport 及 ServiceSupportImpl 的存在。可是我看到的程式邏輯卻是 Client 透過 BizService 傳回 ServiceSupport的實例,然後再直接呼叫它存取需要的資料,所以這個程式違反不跟陌生人打交道的設計原則。

這樣一來,薄化了 BizService 的責任,卻又厚化了 Client 的程式邏輯;站在 Services 封裝的角度來看,封裝不足,造成前端必須知道後端的實作細節、及前後端的高度耦合,將會使軟體系統的設計開始變得複雜。站在技術實作的角度而言,雖然傳回共用的控制物件是一種軟體元件再用性的考量,但在領域概念設計的角度而言,缺乏 Service 的封裝,軟體開發的設計情境將充斥著技術語意,徒增系統設計不同觀點的溝通成本與需求變動所帶來的風險,這樣的設計其實是很令人憂心的。

所以,關鍵在抽象化,尤其是在設計語意上須重視領域概念上的封裝,正所謂,聖人作而萬物覩;本乎天者親上,本乎地者親下,則各從其類也。應賦予每個軟體元件適當的責任,不要讓它們跟陌生人打交道呀。

Powered by ScribeFire.

Please follow and like us:
分類: 分析設計建模, 易經思維, 設計原則, 軟體開發。這篇內容的永久連結

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *