連載39:軟件體系設計新方向:數學抽象、設計模式、系統架構與方案設計(簡化版)(袁曉河著)
由於對簡單的理解會很多,具有最少構成要素的結構,符合簡單性觀念。在眾多可能中選擇一個最方便的方式,也符合簡單性觀念。根據奧康的剃刀原則“如無必要,勿增實體”即簡單有效的原則。然而簡單性是一個相對的概念,是在不同的時空、不同的視角下存在的一種可被成本最低的理解。
但是在系統構架中,具有簡單的設計方案,往往具有最少的約束,從而帶來最為直接的處理方式,由於簡單,所以設計開發都顯得容易掌控,其穩定性和可靠性會大大的增強,同時由於簡單,所以一旦存在需要擴展,其擴展的約束也是非常少,所以能夠形成新的擴展。當然,如果我們能夠通過統一劃歸的手段,那麽最終的系統也是從復雜趨向簡單,統一性是根本的,而簡單性則是期望獲得的一個結果。統一化以後,容易產生簡單的處理,例如我們的線性化處理,最後呈現的整齊劃一的簡單原則。
然而簡單也是容易讓人迷惑,很多時候需要從一個整體去考察其是否簡單,而不是僅僅從局部來考察。但是在很多的系統設計中,由於所有的整體都是一個一個局部構成的,所以在我們關註局部的情況下,我們容易制定一個盡量簡單的策略,然而,當我們將一個個局部整合在一起的時候,卻發現我們最終的系統不是一個簡單的策略,那是否說我們在局部設計的過程中就不要思考簡單原則,當然,我也可以負責任告訴你,最後你的系統肯定不是一個簡單有效的系統。這就形成了一個困境。我們應該在何時使用簡單的策略呢?我們依據的標準是什麽呢?
帶著這樣的困惑和悖論,我們繼續來分析一下我們平時的“化繁為簡”的方法,無論是分析還是綜合的過程中我們是否無時不刻在使用簡單化的方法和手段呢?雖然,目前還無法從理論上論證這個就是一個“完美的”處理方案,但是在巨多的事例面前,我們是否也明白在整個設計過程中,是否應該時刻保持“簡單”的處理原則,即使可能我們已經偏離了我們的目標,但是,從一開始就是沿著“復雜”的方向進行,那麽我們註定是設計出一個無比復雜的系統。
因此我們需要清醒的認識到,一個系統具有的簡潔美,不是因為其內容是簡單的,而是其表達形式是簡單的,其體系結構是簡單的,我們掌握起來所支付的成本應該是最低的,我們變化處理的成本也是最低的。所以我們說,設計一個系統不是創造一個“實體”,而是要求通過我們的設計構建起一個實體的“映射體”,這個“映射體”是一個具有簡單結構通過簡單方式構建在一起能夠完整表達這個實體的外部特征的結構體。讓外在的簡單性和內在的真實性密切相互關聯,達到矛盾的對立和統一。
對稱性可能是簡單性的一個非常重要的表現特征,為什麽呢?因為從直接的效果上來看,我們掌握一個對稱性所需要的“知識量”,只需要“一半”就能夠掌握,比如當我們知道一個圖形滿足對於某個直線對稱,那麽我們可以用已知的的這些信息,完全勾畫出另外的“一半”圖形,雖然對稱的方式有多種多樣的,但是,對稱表示元素的構型在自身變換群下的不變性。這樣的不變性就是我們簡單的體現。
因此,很多時候,我們的設計在能夠具有對稱方式展現的情況下,都應該考慮向作對稱的一面進行設計,就算我們需要增加一些更多的屬性來填補,其實針對整個系統來說,對稱帶來的簡單性的價值遠遠超過增添屬性帶來的成本。例如我們具有如下的一個狀態設計的處理(如圖7-4所示):
圖7-4
此時為了描述這樣的狀態,我們不得不使用如下的方式:
switch(哪一個狀態)
{
case State1:
{
if(condition_1) … … …
}
case State2:
{
if(condition_1)… … …
if(condition_2)… … …
}
… … …
}
如此復雜,我們的外在表達形式就非常復雜,那麽我們可以“補全”這些條件,形成一個對稱的局面呢!
圖7-5
此時,我們引入了一個condition_NoExit條件以後(如圖7-5所示),那麽我們完全可以使用一個狀態變遷的表來表示這些狀態和條件的處理方式(此時,是不是感覺我們的處理方式立刻變得高大上了^_^),由此我們的設計就變得更加簡單,更容易擴展,而且所需要掌握的成本同樣會更少,所以增加並不一定就是代表了復雜,有時候讓之形成“對稱”的效果以後,能夠給我們帶來意想不到的好處。
2.1 單一的誤解
單一職責原理(SRP)解釋為:一個類只能因為一個原因而改變。
這個原則其實是比較坑爹的一個原則,沒有明確原因到底是什麽層級,是哪個範圍的定義,而且如果按照上面的原則寫出來的代碼,可能會讓你更看不懂這樣的代碼,因為所有類都只有一個變化的原因,所以最後其類恐怕都變化成command設計模式一樣都只具有一個execute操作,雖然這將這個類變得非常簡單,但是這個類之外的類與類之間的關系就會變成一個蜘蛛網了。就有某些好事者寫過這樣的代碼,把閱讀的人都繞暈了,但是別人還振振有詞的表示這個符合最基本的單一職責原則呢!
如果我們從更抽象的意義來看看這個設計原則,這個原則是沒有問題的,只是這樣的解釋是不合理的。我們不需要這樣畸形的將之應用到某個具體的類中來描述,而是依照
通過使用單一原則來闡述非對稱加密中密鑰只能作為加密或解密這樣的單一方式,而不是對稱加密中,既具有加密的密鑰又具有解密的密鑰功能,來說明,采用這些單一的加解密密鑰分離的好處,可以具有組合效應的基本要求,由此可通過機構非對稱密鑰對,個人密鑰對一些列CA機制來達到組合,其應用的強大威力。
人類在不成熟時期,老是希望趨於復雜,但隨著認識的深入,簡單才是人類邁入成熟的表現。
Linux中一切設備資源都是文件,網卡、硬盤、內存、進程等等都是文件,都讓其具有簡單統一的讀寫接口。
但就我的理解,單一原則應該是如何進行秩序化,將它們外在的行為表現的一致,這應該才是單一原則的最核心的問題,我們從表驅動來看,通過秩序化以後,其通過查表就能更容易寫入(舉例表驅動的例子),比如在java中為什麽有一個CObject,就是為了化為一致的處理(具體分析CObject的成員變量),才容易更好的規範和組合。又例如組合設計模式(將組合設計模式解釋一下),就是通過規範秩序以後,才能夠更好的將這些行為組合在一起。這些都體現了單一化、秩序化的思想。所以,不是因為我們管理的數量多,而是我們管理是一致的,可通過簡單的操作就能夠完成。故,兵法上說,指揮千軍萬馬就如同指揮一個人。
然而,我們在軟件設計中的單一原則的認識還比較膚淺,因為對於單一原則來說,其實表示的對象的狀態和定義是單一的還是其具有的操作是單一的呢?在軟件設計的思維角度中其表示的是狀態和定義是單一的,但是我們在實際的過程中卻大大的限制了其自身的可擴展和靈活變化。
這裏我們以Java的integer類來看,對於整數這樣的一個對象,其接口滿足我們剛才講到的單一原則,因為其表達的是對整數的設置、獲取、基本的四則運算、序列化、向string轉換運算等,但是這個職責單一的類無法包括其新的特性,例如,對於整數來說,它是一個數學意義上的“環”,這些特性是整數的核心特性,其實包含的是內部的一個稟性,但是我們的軟件設計過程中,無法預先定義這樣的接口,我們要求的單一原則,不會對將來可能存在的操作進行接口定義,其實我們也無法在現有的設計語言的技術上更好的對接口進行完整的確定,因為這個對象的特性會隨著需求在變化,而我們目前的軟件設計還只是使用形式決定內容的層次上(註:在哲學和科學中都是內容決定形式)。是通過外在的接口來定義一個對象,而不是通過其概念定義到內部特征,再到外延這樣的順序來進行,所以,我們永遠都在更改我們的接口,永遠都無法達到單一原則的要求。所以,我們面向對象的思路是否還存在一些致命的弱點呢?
小心為“名”所困,一個類只有一個因素讓其變化,即類具有單一原則,然而目前的情況是凡是能通過技術手段將類劃分成多個類就劃分成多個類,於是在一個小工程裏面就出現成千上萬個類,更有情況是一個類只有一個成員變量,一個Set一個Get操作,類無獨立的邏輯意義,成了美其名曰解除耦合,同時類與類之間的關系非常復雜,任何一處修改都要去查查類之間的關系,非常復雜,同時其擴展也是非常復雜的,不知道應該在哪個類中進行繼承和組合。類的個數也是劇增,很難記住如此龐大數量的類名。而且這些類名也大多無實際意義的名稱。於是系統就進入了無人願意維護的地步。這在設計中就是一種過度設計。不僅無法理解,而且也無情的浪費資源。
那麽一個類到底有多大呢?我想任何大師都無法準確回答這個問題。因為類需要表征其邏輯意義。這些邏輯意義可能對應一個物理意義,有可能對應部分的物理意義。但無論如何,只要認為其有獨立的邏輯意義,同時在認識能力的範圍內,那麽其類就達到目標。另外,在需求變化的過程中,其變化以後只要改變其邏輯意義,那麽就需要劃分成兩個或多個類,所以變化的因素很重要,當變化沖擊了起類的邏輯意義,其重構就要開展。
在一個系統中,不是將類劃分的越小,就越能夠表示解除了耦合。在類設計中同時也要考慮到其內聚性,高內聚目前是很多設計中缺失。只有高內聚才能保證其邏輯意義獨立。類之間交互才能減少。讓類達到一個平衡。
盡信書則不如無書,類的粒度只有從設計的角度去思考才能設計出優秀的系統,走向極端只能使事業失敗的風險變成必然。
2.2 簡單化設計原則
Unix設計原則:
ü 清晰原則:使用簡單接口拼合簡單部件。編程的本質就是要控制復雜度,多數問題只會局限於一個局部,不會影響全局。unix向TCP/IP協議棧實現socket鏈路層接口管理框架。
ü 接口單一:一個接口包含一個功能。例如Rx/Tx/IOCtrl
ü 組合原則:設計時候考慮組合拼接。把復雜問題分解為一條程序處理鏈條。程序之間可以通信、前者的輸出是後者的輸入。通信方式盡量采用簡單文本格式,人可以方便閱讀。組合程序之間不進行內部狀態依賴,互相獨立,對處理鏈上遊和下遊程序不做假設可以替換。例如Unix管道技術,通過管道組合可以完成復雜的工作IS|WC
ü 分離原則:策略與機制分離,接口同引擎分離。策略是程序的外部表現,體現用戶界面。機制是內部實現,體現核心業務邏輯,相對穩定。實現時把策略和機制分離,策略作為前端、後端是機制,二者通過IPC進行通信,同樣的機制可以變換多種界面,滿足不同的需求,同時機制保持穩定。例:Xwindow設計/unix多種shell機制,GUI界面程序設計/INVX架構,VRPV8 SMP分層模型:DOM/D2M/DAM,一種D2M適應多種工具和眾多APP
ü 透明原則:設計和實現都要可見。設計簡單,包括流程/數據結構/接口,在代碼級別容易理解,運行時刻通過輸出信息或者查看運行狀態,容易監控程序是否正確執行。作為設計的一部分,考慮支持狀態查看和輸出重要信息
ü (New)表示原則:把知識疊入數據,簡化和統一處理邏輯。
ü 簡單統一原則:Unix中一切設備資源都是文件,網卡、硬盤、內存、進程等等都是文件,都讓其具有簡單統一的讀寫接口。
首先,讓我們來從層次層面進行分析簡單化為什麽如此重要,同時也從一個更加全面並且深入的角度分析簡單化設計原則,“簡單化”其實並不“簡單”。
在一個具有層次化的系統之中,底層的架構原則往往是簡單的,也就是構建起最為基本的架構思想是“簡單”的,例如我們使用“置換”這一公理化的方式來表達軟件設計基礎的體系架構,這些架構是簡單的,並且可以更好的論證,隨著系統的層次越向上,其“簡單化”就逐漸被復雜所取代,越向上其將具有過多的“特征”,更為復雜的因素來決定。
雖然,最底部基層的“簡單”構建,但是需要註意,簡單化是最具有區域限制的,這一點過去一直被忽略,為什麽簡單化具有區域限制呢?因為一旦大範圍的領域,其更多的具有更為復雜的特征制約。舉一個不太恰當的例子:AK47是一款簡單化體現極致的士兵單人×××,但是,其無法做到跨大區域的完全應用,其在大領域內還需要更為復雜的海陸空的堅船利炮來決定,而在我們的系統設計過程中,為了局部的更優化更具體更特殊的處理,我們會應用更為簡單有效的解決方案,但是,一旦其領域擴大,則往往警惕我們需要使用更為復雜的上層設計來適應大範圍的要求。
另外在縱向方面,抽象這一層面上,我們也可以忽略其他的因素,聚焦於某一固定的特性上,那麽此時這一固有的特性也體現了“簡單”化的設計,此可稱呼為“抽象簡單化”,這樣的簡單原則則是有必要的前提條件,是在某一定的假設情況下具有的簡單化。而我們的很多接口往往就體現這樣的簡單化要求。如果我們體現的抽象簡單化體現的越是周到,則表示我們的接口設計則更為優秀,讓接口更加有效的獨立,也讓系統變化過程中,接口才能夠達到更好的適應性。但是這裏需要註意,抽象簡單化是有前提的,不是任意的,因此需要關註與其前提的適應場景,另外抽象簡單化也是具有區域限制,是在某一特殊區域內具有適應性,所以接口也會根據區域的變化而發生變化。
數據建模,使用數據之間的關聯關系來體現業務邏輯或者領域知識,使得業務處理邏輯代碼簡單一致穩定。
僅僅改變數據模型就可以支持新業務,邏輯處理代碼不用修改。
通過使用單一原則來闡述非對稱加密中密鑰只能作為加密或解密這樣的單一方式,而不是對稱加密中,既具有加密的密鑰又具有解密的密鑰功能,來說明,采用這些單一的加解密密鑰分離的好處,可以具有組合效應的基本要求,由此可通過機構非對稱密鑰對,個人密鑰對一些列CA機制來達到組合,其應用的強大威力。
限定PW兩端是相同類型的業務(對稱的)這樣簡化了PW的設計。
對於一些狀態信令,PE必須參與或監控,有時還可能需要透傳、轉換、記錄這些控制,狀態信息。
一個隧道上可多條偽線(PW)進行復用。
組網中的方案和學問、技巧、困難、目標、需求和能力提升點:
傳送速率越來越快,瞬間接入越來越多,高清VoD,在線授課、雲服務等等。
電信網絡扁平化,無線網絡異構化,降成本、統一架構、輕松運維、面向未來的可擴展性和靈活性。
快速部署、即插即用、快速故障定位、配置簡單、Qos保障、靈活接入,能夠組大網,集中控制,集中配置,集中管理。
業務性能管理。實時或周期性業務監控,高精度、業務報文直接度量,時鐘同步和管理、可視化、圖形化人機管理界面、快速故障定位、端到端IPSec安全方案、提供虛擬化的軟件平臺。
存在多條路徑,雖然是動態查找到的,但是其無法做到最優,可以通過mpls建立一條最快速有效,同時最安全的路徑,這條路徑上其設備可以得到保障,是最優的路徑選擇,而且也是動態生成,只是生成一條最優路徑,而不是每個包都去找最優的路徑,於是其Qos和安全能夠得到保證。而且其控制和管理也變得簡單,而且對原有網絡不需要做大的更換。
MPLS轉發,其控制和轉發功能相剝離,控制平面負責路由器信息的建立,標簽信息的建立,轉發平面負責IP轉發和標簽映射,並且負責標簽轉發。下面是MPLS的轉發過程在所有的LSR(標簽轉發路由器)路由器上啟用路由協議,在LSR中建立路由表項。
利用LDP(標簽分發協議)利用IP路由表建立LSP(標簽轉發通道)LER(標簽邊緣路由器)收到IP報文後,分析報文頭,對應到FEC(轉發等價類)然後給報文加上標簽,根據標簽轉發表中的LSP信息,將報文送到相應的接口,LSR收到帶有標簽的報文,只歐諾個解析最外層標簽頭,然後根據標簽頭查找自己的LSP。之後替換掉標簽頭,送到相應的接口,其余的LSP處理過程相同,當倒數第二跳的LSR收到帶有標簽的報文,查找標簽轉發表,然後判斷對應的出口標簽是否為隱式標簽或者空標簽,然後彈出標簽,發送IP報文到最後的LSRLER收到倒數第二跳發來的報文後,執行三層路由功能,根據報文的目的地址進行轉發。
提前分庫、分表,遷移到多個硬件設備,運維工作量會更少,比使用hash算法更好處理
No SQL也是這樣的思想,不以結構化來綁定其自己的策略。
為什麽TDM業務通過IP網絡中需要建立一個隧道呢?這與TDM業務有關,因為其是同步網絡,有先後順序,沒有重傳機制,所以建立隧道與業務特性無關。
網絡中業務平面與控制平面解耦,也是單一原則的體現。通過使用簡化的設計方案能夠將不必要的耦合降低最小,而MPLS為什麽能夠做到更為復雜的多協議的接入呢?其實其內涵就是簡化機制、分離解耦。而這思想其實是借鑒了TDM的優秀思想,也是充分利用了簡單的設計原則,反而讓其具有支撐更為復雜的業務處理和完善的管理能力。
人類在不成熟時期,老是希望趨於復雜,但隨著認識的深入,簡單才是人類邁入成熟的表現。從復雜中抽象出簡單,其實這是一個困難的過程,一旦抽離簡單化成功,則我們的認識就會突破到新的境界。
其實簡單原則的設計,穩定性強,但缺點在於無法復用,例如美國的航天飛機和中國的航天飛機就是這兩個極端,由於為了達到其高度的可中用性,美國的航天飛機創作復雜,那個華麗的外型代表了美國的先進科技,但是航天飛機的可靠性就很低了,爆了好幾個,而且是每發射一次都提心吊膽,其航天飛船則方便得多,始終回來以後就可以保留到博物館。但是並不是設計的越復雜其可復用性就越好,例如AK47,就三大塊,但是其設計簡便靈活,非常實用,而且不卡殼,結構簡單,安裝快捷,所以簡單就是美,不同的組件之間不要過多的依賴,在需要經久耐用的地方就要固化設計,在需要可靈活應對變化的地方就要設計到可靈活裝拆,其組件間可單獨創建和銷毀。
連載39:軟件體系設計新方向:數學抽象、設計模式、系統架構與方案設計(簡化版)(袁曉河著)