連載35:軟件體系設計新方向:數學抽象、設計模式、系統架構與方案設計(簡化版)(袁曉河著)

分離機制

 

“道生一、一生二、二生三、三生萬物”《道德經》

分離機制的目標是爲了更好的解除系統的耦合,規範其處理的機制。然而,我們不要將此理解爲一切都是爲了分離,需要明白的是,我們的任何分離的最終目的就是爲了更好的合併。分離和合並是達到目標而使用的方法和手段,是矛盾的兩個方面。如果沒有根據的“分”,那麼其就無法達到有效的“合”,所以分合是相生相剋的。閒話一點:道德經中爲了更好的闡述這樣的原則,所以是用“生”來表達。其實就是要理解“分合”的統一性。

所以,下面所舉的幾個維度上的分離,最終也要求將部分進行獨立以後進行機制化處理,讓其不耦合於具體的分化,通過獨立出機制以後,具有能夠融入更多的具體劃分的能力,這也是相輔相成的過程。

關注點分離:

解耦的根本目的和效果是:

1、 把變與不變隔離,把相互獨立的不同的變化隔離。

2、 關注點隔離(有利於項目並行開發,獨立維護)

好的架構從需求開始,需求是驅動力。

軟件演進需要同時關注商業和技術兩個視角,因爲刺激來自於這兩個方面,如環境、組織、進程、技術和利益相關人的需要,這些變更刺激反應爲軟件結構或(和)功能,通常一個業務系統的複雜性來自於併發控制盒異常處理兩個方面。

基於事件的發佈  訂閱模型

發佈者和訂閱者在三個層面進行解耦:

1、 時間上解耦:發佈者和訂閱者不必同時在線,它們之間不必同時參與交互。

2、 空間上解耦:發佈者和訂閱者不必相互知道對方所在的位置,發佈者和訂閱者不需要擁有直接到對方的引用,也不必知道有多少訂閱者或發佈者參與交互。

3、 同步上解耦:發佈者和訂閱者是異步模式,發佈者不斷的產生事件,而訂閱者則可以異步的得到產生事件的通知。

訂閱發佈機制的解耦,消除了依賴,增加了系統的可擴展性

 

數據結構和操作分離

 

下面我們討論分離數據結構和操作機制。

這個機制體現的最爲明顯的是STL,雖然在面向對象中將數據和操作捆綁在一起,形成一個對象的整體,但是這個世界永遠都是殊途同歸,在STL中將數據結構和操作完全分離,也一樣能夠達到目的,而且也開創了一個不一樣的新天地。

在面向對象中將數據與算法結合起來,形成我們概念上認識的對象。但是對對象的認識方式是我們唯一的方式嗎?答案是否定的,在STL中展示的是另一種認識方式,這種方式是將所有事物的共性抽象出來(不是面向對象中將對象體抽象出來),這些事物包含形態(數據結構)和操作(算法),也就是說形態和操作都可以獨立地被抽象出來,它們其實並非相互依賴,也就是說是獨立存在的,於是在STL中大家可以看到有容器的概念,也有函數對象(操作的抽象)的概念,由於不是相互依賴所以這兩個概念就是可正交的,可以分屬於不同維度的變化,就有點像座標中的X,Y。這樣劃分以後,大家的代碼實例化以後就是將XY賦值,兩個維度上就形成具體類(包括數據結構和算法)。我們說這種處理方式就是一個泛化的過程(一般化的過程),於是開拓出一個新的領域---泛型編程。

當然,這裏如果我們針對操作進行進一步的抽象和處理,那麼可以讓之轉換爲算法的處理,由此數據結構和操作的分離,就可以轉換爲數據結構和算法的相互分離。而在泛型編程中這種思想體現的非常突出。

使用這樣分離的優勢就在於數據結構和操作獨立變化,只要支持相互之間連接的處理,例如迭代器、函數對象等,則就能達到獨立變化的目的。分離以後能夠讓我們具有選擇的好處,一旦此數據結構或算法不能滿足要求,則我們可以進行替換,而且能夠達到替換以後也能夠正常的運轉。

 

控制與業務分離


如果將控制和業務兩者混雜在一起,那麼會導致因爲業務上的功能變化(擴展原有業務能力)或控制方式的變化(例如集中方式轉化爲分佈式方式),都會導致散彈式的修改代碼,此時我們應該懷疑我們已經將業務和控制耦合在一起。那麼如何進行分離呢?其實主要的是如何識別哪些是業務,哪些是控制處理,這裏讓我們先從最底層的代碼來看看。

這樣我們通過接口的方式將業務處理進行抽象,讓控制和業務在類的級別上進行分離,當然在更高的層次上,我們也可以通過使用相關的組件方式,將業務處理獨立成一個特性模塊,通過動態加載方式來進行加載,這樣能夠更好進行控制和業務分離。

然而,很多時候我們是很難將控制和業務區別開的,因爲業務需要受環境等影響,例如我們在客戶機上進行緩存,於是我們需要在業務處理中進行一個CRC校驗,或者進行簽名驗證,此時這個校驗是業務還是控制,很難進行區分,但是這裏有一個好的處理方式就是講這種具有模棱兩可的東西獨立成新的特性,通過中間處理的方式和控制以及業務進行交互。

 

目標和策略分離

 

當這些列表中的記錄處理大體一致,但是還存在一些較少的差異時,此時解決方式使用再註冊的方式。但這個接口不能混入策略接口中,因爲其不屬於策略接口,其判定的原則就是這個接口是否是策略調用者可選的,如果可選則其爲策略信息,而此處理應該是不同記錄信息間在處理機制的不同,如圖3-5所示:

 blob.png

 

3-5

 

實現這種機制和策略的分離有很多方法,而且很多時候如果我們換一種角度去看也許更能有一種豁然開朗的感覺。上的分離方式是按照strategy的設計模式來處理的,其實我們使用一個“躺”作的proxy方式也能實現這種分離,如圖3-6所示。

 

blob.png 

3-6

 

機制和策略分離中我們需要明確,這種分離以後,機制往往是一些非常原始的操作行爲,這樣行爲可能正在策略方面看來是多麼的簡單和不可思議。

例如,在操作系統中計算機是如何做出併發處這樣的,在底層,機制處理其實就是一種分時的處理機制,將系統化成不同時間片的處理,而策略就呈現五花八門的情況,例如可以使用進程進行併發的處理,也可以採用線程的併發,而針對這些進程間或者線程間的調度可以採用最適合當前應用環境的策略,或者可以將線程劃分爲內核態和用戶態不同的層次,然後通過保護現場的方式,來連接機制和策略的共同作用,所以在這樣的處理中機制其實需要非常的“淳樸”,不需要過多的考慮如何進行策略變化。而在現實中我們往往在考慮機制的過程中過多的考慮了策略的變化,導致了整個系統分離程度不夠而導致系統非常高的耦合。那麼我們有哪些更值得推薦的方法來進行機制和策略的建立以及相互之間界限的劃分和分離呢?

首先,機制最好是完整的和緊湊的,就像我們數學中的實數一樣。很多時候我們對其進行的處理機制最好是對稱的,因爲對稱的方式才能讓策略展開靈活性,同時也是緊湊的,多餘的處理是不存在的,這些機制的設計多一個和少一個都是不正確的,而且相互之間是獨立的。

其次,機制的操作最好是原始的,考慮的是其最基本的元素。

再次,機制和策略最好能夠有一個明晰的分層,而且此層次中,機制的內部實現和策略的內部實現可以不用暴露給對方,要有封裝,需要提供的服務收攏爲單一的接口,至少機制可以使用接口方式來提供,這樣其耦合性就會相對更小。

 

動態和靜態分離

 

將動態和靜態結合在一起,其實更能夠完成一些絕妙的好處。例如java語言中的反射機制,就是通過動態的方式,獲取到靜態的類型信息,通過這樣的處理,能夠在動態的方式下進行動態類的創建。

將靜態和動態進行分離的一個優勢之一就是需要利用靜態的方法進行檢測和驗證,如果是在動態情況下,所花費的成本高昂,取得的效果卻非常低下。而如何進行有效的分離呢?那就是如何更好的通過靜態來“置換”動態。

過去的很多描述都是靜態結構,其結構決定了其架構,但是其靜態結構不是全部,動態結構是一個更廣闊的話題,在我們所涉及的結構中有哪些是動態結構呢?如何將靜態結構轉化爲動態結構呢?其動態結構是否具有不斷的平衡和演化的能力?

首先要有動態結構的機制:1MFC的動態類(繼承體制)。2Java中依賴注入。這裏我們如何設計出能夠根據需求和運行時實際的變化能夠將其結構發生變化以適應新的環境呢?

從對這些動態結構的分析,然後反向給出這些機制,提供新的需求和問題,讓機制更加完善和有效。

ü 繼承與組合相互之間的動態演變。

ü 關注點分離在動態結構中的演變,一旦其某個橫切點上發生變化,於是就產生策略的設計模式結構。

抽象工廠的缺點,難以支持新種類的產品,難以擴展抽象工廠以生產新種類的產品,這是因爲抽象工廠接口確定了可以被創建的產品集合,支持新種類的產品需要擴展該工廠接口,這將設計抽象工廠類及其所以子類的改變。所以STL函數對象,就是一個簡單的訪問者模式,還是一個command設計模式。

在現在互聯網進行的灰度發佈和快速擴展,其所使用的設計方法和思想也是將靜態和動態進行有效的分離,也就是說不受靜態結構的限制,向一些動態加載變化結構,這可有助於進行有效的灰度升級。

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章