1、基本原則 在開始設計之前,考慮主要的設計原則將有助於找到架構的設計的“最佳方案”,降低成本和維護需要,提高系統的可用性和可擴展性。主要的設計原則如下: 關鍵點的分離:將應用程序分成清楚的不同元素,使功能的重疊盡可能的少。 單一責任原則:每一個組件或模塊應該只負責唯一一個特定的功能。 最少知識原則:一個組件或對象應該不用知道其他組件的內部實現細節,而只要按照彼此的約定調用即可。 不要重復自己:一個組件對應提供一個功能,一個功能也只應由一個組件提供。而不能將功能的實現分散到很多其他的組件中去。 避免在前期做大量的設計:如果你的需求不是很清楚,或者有設計隨時間演變的可能,應當避免在項目前期做大量的設計工作。這一設計原則通常被叫做“BDUF”。 多用組合少用繼承:在盡可能的情況下,使用組合的方式來重用功能,而不使用繼承的方式。因為繼承增加了父子類之間的依賴關系,限制了子類的重用。 2、設計要點 在設計軟件或系統時,軟件架構的目標就是通過將設計分割為不同的關注領域來降低其復雜性。例如,用戶接口、業務進程和數據訪問均可視為不同的關注領域。在每個領域內部,組件應專注於其特定的領域,而不應該混合其他領域的代碼。例如,UI進程組件不應該包括直接訪問數據源的代碼,而應該使用其他的業務組件或數據訪問組件檢索數據。 下面列出了設置應用程序的指導方針: 避免在前期做所有的設計:如果系統需求不清楚或存在設計演變的可能,在前期不去做一個完整的設計應該是一個好主意。在項目進程中演化設計應該是一個恰當的方法。 分割關注領域:將應用程序分成清楚的不同元素,使功能的重疊盡可能的少。這種方法的好處是一個特征或功能獨立於其他的特征或功能,可以實現最優。還有,如果一個特征失敗了,不會導致其他的特征也失敗,它們之間彼此獨立運行,互不影響。這種方法也有助於應用程序的設計和理解,便於復雜的互相依賴的系統的管理。 每個組件或模塊應有單一的責任:每一個組件或模塊應該負責實現特定的特征或功能。這可以使組件實現內聚且更容易優化和變更。 一個組件或對象不應該依賴其他組件或對象的內部細節:組件或對象調用其他組件或對象的方法,這些被調用的方法應該說明如何去使用它。如果需要,將這些內容放在子組件或其他獨立的組件中。這將有助於開發一個具有可維護性和適應性的應用程序。 在一個應用程序內部不要復制功能:應該只有一個組件提供特定的功能——這個功能不應該在其他的組件中出現。在一個應用程序中復制功能會使功能的變更很難維護,削弱了程序的條理性,引入了潛在的矛盾。 確定應用程序組件的組成部分:實現這一點的最好方法是確定符合你情況的模式,檢查這些模式所使用的組件的類型。例如,一個小型的應用程序可能不需要一個業務工作流或者UI進程組件。 組織不同類型的組件到各自的邏輯層:在一開始,確定不同的關注領域,然后組織相互關聯組件到合適的邏輯層中。 保持層間設計模式的一致性:在一個邏輯層中,組件的設計對於一類特定的功能應該是一貫的,有延續性的。例如,如果你選擇使用表數據網關的模式創建對象,你就不應該再使用其他的方式來訪問數據和初始化業務對象。 在同一邏輯層中不應混合不同類型的組件:例如,UI層不應該包含業務進程組件,而應該包括處理用戶輸入和處理用戶請求的組件。 確定哪類分層要強制執行:在一個嚴格的分層系統中,A層中的組件不能調用C層中的組件,它應總是調用層B中的組件。而在一個相對寬松的分層系統中,一個層中的組件可以調用其下層中的組件,而不只是正下層中的組件。在任何情況下,都應該避免對上層的調用和依賴。 抽象實現層間的松散耦合通過定義接口,層內的組件間可以以一種共知的方式彼此進行請求和響應,這樣的實現相對比較靈活。另外,你也可以用接口或抽象類來定義公共的接口或者抽取公共的部分(依賴倒置)。 一個組件不要承載太多的功能:例如,一個UI進程組件不應該包括數據訪問代碼。一種常見的違反模式的情況叫做Blob(一團),在這種情況下,基類提供了過多的功能。一個“團”對象通常有很多函數和屬性用來提供混合了日志和異常處理的業務功能。由於要處理子功能的各種變體,做復雜的初始化,這類對象通常非常龐大。最終結果是,這種的設計很容易產生錯誤,而且很難維護。 清楚組件間是如何通信的:這首先需要了解你的應用程序是如何部署的。需要明確的是,組件間的通信是否需要跨越物理邊界或跨進程,所有組件是否運行在統一進程當中。 多用組合少用繼承:在盡可能的情況下,使用組合的方式來重用功能,而不使用繼承的方式。因為繼承增加了父子類之間的依賴關系,限制了子類的重用。這也減少了繼承的層級,避免了去處理復雜的層級關系。 保持層內或組件內數據格式的一致性:混亂的數據格式會使程序很難運行、擴展和維護。每次你都需要在不同格式間進行轉換,執行轉換的代碼。這樣減低了性能,而且沒有必要。 盡可能的將交叉(橫切)代碼從業務邏輯中抽象出來:交叉(橫切)代碼通常涉及到安全性、通信和操作管理(例如,日志和檢測)。將這些代碼混合到業務邏輯中會導致程序擴展和維護上的困難。修改這些交叉(橫切)代碼會牽聯到所有混合了它們的業務邏輯代碼的修改。可以考慮使用框架來實現交叉(橫切)的集中處理。 命名習慣的統一:看看團隊、組織里是否建立了相關的命名規定,如果沒有,你應該建立一個公用的命名標准。由此而來的代碼的一致性會使團隊成員檢查代碼更容易。這樣也更易維護。 建立異常處理的標准:例如,應該總是在層的邊界捕獲異常,不應該在層內捕獲異常,除非你可以在層內處理它,也不應該用異常來實現業務邏輯。該標准還應包括錯誤提示、記錄日志的策略和對異常的檢測 3、架構框架 下面的表格列出了你在設計架構時應該考慮的主要方面。通過這些關鍵的問題了解通常會犯錯的地方。這個章節為這些不同的方面提供了指導。 表格1架構框架
認證和授權
缺少跨信任邊界的認證
缺少跨信任邊界的授權
松散或不適當的授權
緩存
數據緩存反復無常
緩存敏感數據
選擇了錯誤的緩存方式
通信
選擇了錯誤的傳輸協議
多余的跨物理和進程邊界的通信
沒有有效的保護敏感數據
組合
彼此協作的應用模塊之間相互依賴使開發、測試和維護變得很困難。
模塊間的依賴變更強制代碼重新編譯和模塊的重新部署
頑固的代碼依賴使動態的UI布局和更新變得很困難
頑固的代碼依賴使模塊的動態加載變得很困難
並發和事務處理
沒有保護對靜態數據的並發訪問
死鎖導致不適當的鎖定
沒有選擇適當的並發處理模式
長期保持對數據的鎖定
不恰當的使用互鎖
配置管理
缺少或存在錯誤的配置信息
沒有對敏感的配置信息采取保護措施
無限制的對配置信息的訪問
鏈接和聚合
錯誤的功能分組
關注點的分離不清楚
層間的鏈接過於緊密
數據訪問
對總用戶的不必要的驗證和授權
多余的對數據庫的訪問
業務邏輯摻雜數據訪問代碼
異常管理
處於不穩定狀態
向最終用戶暴露敏感信息
使用異常控制程序流程
沒有記錄有關異常足夠的細節
分層
對組件進行了錯誤的分層
沒有遵守分層和從屬划分的規則
沒有考慮層間的物理分布
日志和檢測
缺少日志和檢測
日志和檢測太過細致
沒有將日志和檢測做成運行時可配置的
沒有阻止和處理日志記錄失敗的發生
沒有對關鍵的業務進行日志記錄
狀態管理:
使用了錯誤的狀態存儲
沒有考慮序列化的需要
沒有在需要的時候保持狀態
結構:
針對方案選擇了錯誤的結構
創建了一個過於復雜的結構,而這是沒有必要的
沒有考慮部署方案
用戶體驗:
沒有遵循發布的指導方針
沒有考慮易用性
對不相關的函數創建重載接口
驗證:
缺少跨信任邊界的驗證
沒有對范圍、類型、格式和長度進行有效的驗證
沒有重用驗證邏輯
工作流:
沒有考慮管理的需求
錯誤選擇工作流模式
沒有考慮異常狀態及對它們的處理