軟件設計概述

from:http://blog.csdn.net/guoxiaoqian8028/article/details/24204273

概述

軟件設計是把需求轉化爲軟件系統的最重要的環節,系統設計的優劣在根本上決定了軟件系統的質量。

在此,主要闡述軟件系統設計的5個核心內容:體系結構設計、用戶界面設計、數據庫設計、模塊設計、數據結構和算法設計。旨在幫助開發人員搞清楚“設計什麼”以及“如何設計”。

一般把設計過程劃分爲兩個階段:概要設計階段和詳細設計階段,如下所示:

  • 概要設計階段的重點是體系結構設計。
  • 詳細設計階段的重點是用戶界面設計、數據庫設計、模塊設計、數據結構與算法設計等。

可根據項目的情況進行文檔裁減和過程合併,如項目開發過程只有一個設計階段和設計文檔。

體系結構

體系結構如同人的骨架。如果某個傢伙的骨架是猴子,那麼無論怎樣餵養和美容,這傢伙始終都是猴子,不會成爲人。

由此可見,體系結構乃是系統設計的重中之重。

目前業界比較流行的軟件結構模式有C/S(客戶/服務器)、B/S(BROWSE/SERVER)、層次結構(上下級層次結構、順序相鄰的層次結構、含中間件的層次結構)

體系結構設計原則

 合適性

即體系結構是否適合於軟件的“功能性需求”和“非功能性需求”。高水平的設計師高就高在“設計出恰好滿足客戶需求的軟件,並且使開發方和客戶方獲取最大的利益,而不是不惜代價設計出最先進的軟件。

 結構穩定性

詳細設計階段的工作如用戶界面設計、數據庫設計、模塊設計、數據結構與算法設計等等,都是在體系結構確定之後開展的,而編程和測試則是更後面的工作,因此體系結構應在一定的時間內保持穩定。

軟件開發最怕的就是需求變化,但“需求會發生變化”是個無法逃避的現實。人們希望在需求發生變化時,最好只對軟件做些皮皮毛毛的修改,可千萬別改動軟件的體系結構。如果當需求發生變化時,程序員不得不去修改軟件的體系結構,那麼這個軟件的系統設計是失敗的。

高水平的設計師應當能夠分析需求文檔,判斷出哪些需求是穩定不變的,哪些需求是可能變動的。於是根據那些穩定不變的需求設計體系結構,而根據那些可變的需求設計軟件的“可擴展性”。

 可擴展性

可擴展性是指軟件擴展新功能的容易程度。可擴展性越好,表示軟件適應“變化”的能力越強。

可擴展性越來越重要,這是由現代軟件的商業模式決定的:

  • 社會的商業越發達,需求變化就越快。需求變化必將導致修改(或者擴展)軟件的功能,現代軟件的規模和複雜性要比十年前的大得多(對比一下操作系統的變化就明白了),如果軟件的可擴展性比較差的話,那麼修改(或者擴展)功能的代價會很高。
  • 現代軟件產品通常採用“增量開發模式”,開發商不斷地推出軟件產品的新版本,從而不斷地獲取增值利潤。如果軟件的可擴展性比較差的話,每次開發新版本的代價就會很高。雖然開發商抓住了商機,但卻由於設計水平差而導致沒有賺取多少利潤,真是要活活氣死。

 可複用性

由經驗可知,通常在一個新系統中,大部分的內容是成熟的,只有小部分內容是創新的。一般地可以相信成熟的東西總是比較可靠的(即具有高質量),而大量成熟的工作可以通過複用來快速實現(即具有高生產率)。

可複用性是設計出來的,而不是偶然碰到的。要使體系結構具有良好的可複用性,設計師應當分析應用域的共性問題,然後設計出一種通用的體系結構模式,這樣的體系結構纔可以被複用。

用 戶 界 面 設 

爲了提高用戶界面的易用性和美觀程度,總結了十個設計原則。用於提高易用性的界面設計原則有8個:

  • 用戶界面適合於軟件的功能
  • 容易理解
  • 風格一致
  • 及時反饋信息
  • 出錯處理
  • 適應各種用戶
  • 國際化
  • 個性化

用於提高美觀程度的設計原則有:

  • 合理的佈局
  • 和諧的色彩

 用戶界面適合於軟件的功能

用戶界面的合適性是指界面與軟件功能相融洽的程度。軟件的功能需要通過用戶界面來展現,用戶界面一定要適合於軟件的功能,這是最基本的要求。界面的合適性既提倡外美內秀,又強調恰如其分。

 容易理解

提高用戶界面可理解性的一些規則如下:

  • 界面中的所有元素(如菜單、工具條等)沒有錯誤,也不會讓人誤解。
  • 所有的界面元素應當提供充分而必要的提示,例如當鼠標移動到工具條上的某個圖標按鈕時,應當在該圖標旁邊出現功能提示。
  • 界面結構能夠清晰地反映工作流程,以便用戶按部就班地操作。
  • 對於複雜的用戶界面而言,最好提供界面“嚮導”,及時讓用戶知道自己在界面結構中所處的位置。例如對於基於Web的應用軟件,應該在界面上顯示“當前位置”,否則用戶很容易在衆多的頁面中迷失方向。

● 風格一致

風格一致有兩方面的含義:

(1) 一個軟件的用戶界面中,同類的界面元素應當有相同的視感和相同的操作方式。例如命令按鈕是最常見的界面元素,所有命令按鈕的形狀、色彩以及對鼠標的響應方式都是一致的。

(2) 同一類型軟件的用戶界面應當有一定程度的相似性。例如Microsoft公司的Office家族裏有Word、Excel、PowerPoint、Outlook等軟件,這些軟件提供的“複製、剪切、粘貼”功能的操作方式都是相同的。

 及時反饋信息

用戶進行某項操作後,如果過了一會兒(幾秒鐘)用戶界面一點反應都沒有,這將使用戶感到迷茫和不安,因爲他不知道是自己操作錯了還是軟件的原因導致死機了。所以及時反饋信息很重要,至少要讓用戶心裏有數,知道該任務處理得怎麼樣了,有什麼樣的結果。

例如下載一個文件,界面上應當顯示“百分比”或相關數字來表示下載的進度,否則人們不知道要等待多少時間。如果某些事務處理不能提供進度等數據,那麼至少要給出提示信息如“正在處理,請等待…”,最好是提供合適的動畫,讓用戶明白軟件正在幹活、沒有死機。

 出錯處理

在設計用戶界面時必須考慮出錯處理,目的是讓用戶不必爲避免犯錯誤而提心吊膽、小心翼翼地操作。常見的錯誤處理方式有:

  • 提供對輸入數據進行校驗的功能。當用戶輸入錯誤的數據時,及時提醒用戶改正數據。
  • 對於在某些情況下不應該使用的菜單項和命令按鈕,將其“失效”(屏蔽)可以有效防止該項功能被錯誤地使用。例如:對於某些管理軟件,不同的用戶有不同的操作權限。如果低權限的用戶登錄到系統,那些只有高級權限用戶才能使用的功能應當被屏蔽(如變成“灰色”不可操作)。
  • 提供Undo功能,用以撤銷不期望的操作。
  • 執行破壞性的操作之前,應當獲得用戶的確認。例如用戶刪除一個文件時,應當彈出對話框:“真的要刪除該文件嗎”,當用戶確認後才真正刪除文件。

 合理的佈局

首先,界面的佈局應當符合邏輯,最好能夠與工作流程吻合。界面設計人員只有仔細地分析軟件的需求,才能提取對界面佈局有價值的信息。

其次,界面的佈局應當整潔(整齊清爽)。界面元素應當在水平或者垂直方向對齊,行、列的間距保持一致。窗體的尺寸要合適,各種控件不能過分擁擠也不能過分寬鬆。要善於利用窗體和控件的空白,以及分割用的線條。

 和諧的色彩

用戶界面是否美觀,主要取決於該界面的佈局和色彩搭配。實現“合理的佈局”相對比較容易一些,設計和諧的色彩太困難了,因爲色彩的組合千變萬化,並且人們對顏色的喜好也極不相同。

對於廣大軟件開發人員而言,雖然我們沒有必要讓普通軟件的界面漂亮到Windows XP這種程度,但是掌握一些界面色彩的設計原則無疑是非常有益的。

  • 如果不是爲了顯示真實感的圖形和圖像,那麼應當限制一幀屏幕的色彩數目,因爲人們在觀察屏幕的時候很難同時記住多種色彩。
  • 應當根據對象的重要性來選擇顏色,重要的對象應當用醒目的色彩表示。
  • 使用顏色的時候應當保持一致性,例如錯誤提示信息用紅色表示,正常信息用綠色表示,那麼切勿篡用紅色和綠色。
  • 在表達信息時,不要過分依賴顏色,因爲有些用戶是色盲或色弱。

數據庫設計

 開發與平臺無關的數據庫應用程序

目前國際上應用最廣泛的數據庫系統有Oracle、DB2、Informix、Sybase和SQL Server。

這些數據庫系統之間的激烈競爭即有好處又有壞處。競爭的好處是使數據庫系統不斷髮展和完善,並且避免價格壟斷。競爭的最大壞處是逼迫數據庫廠商不斷開發出獨特的功能以吸引更多的用戶,所以各個數據庫系統的獨特功能無法形成統一標準,導致用戶難以開發出與平臺無關的數據庫應用程序,因爲用戶很難抵禦數據庫系統獨特功能的誘惑。

讀者也許會問:“結構化查詢語言(SQL)難道不是數據庫系統的標準嗎?”

是的,SQL是數據庫系統的標準查詢語言。可是數據庫廠商提供了太多超出SQL標準的特色功能,使人們陷入了進退兩難的境地:

  • 如果你想使程序與數據庫平臺無關,那麼只能使用SQL,放棄各個數據庫系統的獨特功能。
  • 如果你超越SQL,使用了某個數據庫系統的獨特功能,那麼這樣的程序就是與平臺相關的。
  • 類似問題也存在於操作系統、Web瀏覽器這些領域。理論上講,只有絕對壟斷才能形成絕對統一的標準,但是人們既希望打破壟斷又希望有統一的標準,這種矛盾無法徹底解決,只能折衷、妥協。建議如下:
  • 如果你開發的是通用的數據庫應用軟件,不想讓應用軟件與特定的數據庫系統捆綁在一起,那麼你就老老實實地用SQL語言寫程序。
  • 如果你開發的是行業專用的數據庫應用軟件,並且這個行業已經指定了數據庫系統(這種局部壟斷現象普遍存在),最近若干年都不會改變的話,那麼你可以超越SQL使用該數據庫系統的獨特功能。

 數據庫性能優化問題

數據庫設計的主要挑戰是“高速處理大容量的數據”。如何優化數據庫的性能是設計人員經常面臨的問題。數據庫性能優化主要有兩種途徑:

  • 優化表結構本身。例如對第三範式的表結構進行反規範化處理,允許表中存在冗餘數據,從而減少多個錶鏈接操作,達到提高性能的目的。
  • 優化數據庫的環境參數。例如提高硬件設施,調整表的空間儘量減少數據碎片等。

在表的物理設計階段,設計人員應當按照第三範式設計表結構(即規範化處理)。這樣做的好處是:表中沒有冗餘數據,表結構很清晰,將來修改或者擴充非常方便。但是按第三範式設計也存在一些缺點:產生了許多表,每個表有相對較少的列,並且這些列必須使用“主健/外健”關聯起來,因此某個查詢操作可能會產生複雜的錶鏈接,導致性能降低。

反規範化處理是指對第三範式的表進行修改,通過合併一些表,或者在表中創建冗餘的列,從而減少錶鏈接操作代價,達到提高性能的目的。要注意的是反規範化處理存在很大的負面影響:管理冗餘數據很麻煩,如果冗餘數據不同步的話,那麼會發生數據錯誤這種嚴重的問題。

所以,對錶進行第三範式的規範化處理是第一重要的,而反規範化處理則需謹慎考慮、不宜過多使用。“規範化處理”以及“反規範化處理”不是自相矛盾之舉,而是性能優化的策略。

除了優化表結構之外,優化數據庫的環境參數也能夠提高數據庫的性能。例如給服務器配置更快的CPU,增加內存。運行數據庫是非常消耗內存的,內存對數據庫性能影響比較大。由於現在市場上的內存條越來越便宜,所以爲服務器配置足夠多的內存恐怕是成本最低、難度最低、見效最快的性能優化方法。

在安裝數據庫系統時,要爲系統指定“塊大小”(一次物理讀寫操作所設計的字節數)。在創建表時,也要爲表指定一定的空間。如果“塊大小”和“表空間”與實際的數據存儲不匹配的話,那麼會產生許多磁盤碎片,這將降低數據庫物理操作的性能。

能否有效地優化應用軟件數據庫的性能,主要取決於開發者對數據庫系統的熟悉程度以及開發經驗。

● 數據庫安全問題

提高軟件系統的安全性應當從“管理”和“技術”兩方面着手。這裏僅考慮技術手段(因爲安全管理超出了軟件工程範疇),一般原則如下:

  • 用戶只能用帳號登陸到應用軟件,通過應用軟件訪問數據庫,而沒有其它途徑可以操作數據庫。
  • 對用戶帳號的密碼進行加密處理,確保在任何地方都不會出現密碼的明文。

確定每個角色對數據庫表的操作權限,如創建、檢索、更新、刪除等。每個角色擁有剛好能夠完成任務的權限,不多也不少。在應用時再爲用戶分配角色,則每個用戶的權限等於他所兼角色的權限之和。

模塊設計

在設計好軟件的體系結構後,就已經在宏觀上明確了各個模塊應具有什麼功能,應放在體系結構的哪個位置。我們習慣地從功能上劃分模塊,保持“功能獨立”是模塊化設計的基本原則。因爲,“功能獨立”的模塊可以降低開發、測試、維護等階段的代價。但是“功能獨立”並不意味着模塊之間保持絕對的孤立。一個系統要完成某項任務,需要各個模塊相互配合才能實現,此時模塊之間就要進行信息交流。

評價模塊設計優劣的三個特徵因素:“信息隱藏”、“內聚與耦合”和“封閉——開放性”。

 信息隱藏

爲了儘量避免某個模塊的行爲去幹擾同一系統中的其它模塊,在設計模塊時就要注意信息隱藏。應該讓模塊僅僅公開必須要讓外界知道的內容,而隱藏其它一切內容。

模塊的信息隱藏可以通過接口設計來實現。接口是模塊的外部特徵,應當公開;而數據結構、算法、實現體等則是模塊的內部特徵,應當隱藏。一個模塊僅提供有限個接口(Interface),執行模塊的功能或與模塊交流信息必須且只須通過調用公有接口來實現。如果模塊是一個C++對象,那麼該模塊的公有接口就對應於對象的公有函數。如果模塊是一個COM對象,那麼該模塊的公有接口就是COM對象的接口。一個COM對象可以有多個接口,而每個接口實質上是一些函數的集合。

 高內聚

內聚(Cohesion)是一個模塊內部各成分之間相關聯程度的度量。內聚程度從低到高大致劃分爲低端、中段和高端,如圖3-15所示。模塊設計者沒有必要確定內聚的精確級別,重要的是儘量爭取高內聚,避免低內聚。

順序性內聚 功能性內聚

時序性內聚 過程性內聚 通訊性內聚

偶然性內聚 邏輯性內聚

低端… 中段… 高端…

各種內聚類型的含義如下:

  • 偶然性內聚。如果一個模塊的各成分之間的關係彼此鬆散(幾乎無關),稱爲偶然性內聚。
  • 邏輯性內聚。幾個邏輯上相關的功能被放在同一模塊中,則稱爲邏輯性內聚。例如一個模塊讀取各種不同類型外設的輸入。
  • 時序性內聚。如果一個模塊內的幾個功能必須在同一時間內執行(如系統初始化),但這些功能只是因爲時間因素關聯在一起,則稱爲時間性內聚。
  • 過程性內聚。如果一個模塊內部的處理成分是相關的,而且這些處理必須以特定的次序執行,則稱爲過程性內聚。
  • 通信內聚。如果一個模塊的所有成分都操作同一數據集或生成同一數據集,則稱爲通信內聚。
  • 順序內聚。如果模塊內的某個成分的輸出作爲另一個成分的輸入,則稱爲順序內聚。
  • 功能內聚。模塊的所有成分對於完成單一的功能都是必須的,則稱爲功能內聚。

 低耦合

耦合(Coupling)是模塊之間依賴程度的度量。內聚和耦合是密切相關的,與其它模塊存在強耦合的模塊通常意味着弱內聚,而強內聚的模塊通常意味着與其它模塊之間存在弱耦合。

耦合的強度依賴於以下幾個因素:(1)一個模塊對另一個模塊的函數調用數量;(2)一個模塊向另一個模塊傳遞的數據量;(3)一個模塊施加到另一個模塊的控制的多少;(4)模塊之間接口的複雜程度。

耦合程度從低到高大致劃分爲低端、中段和高端,如圖3-16所示。模塊設計應當爭取“高內聚、低耦合”,而避免“低內聚、高耦合”。

印記耦合 控制耦合

公共耦合 內容耦合

非直接耦合 數據耦合

低端… 中段… 高端…

各種耦合類型的含義如下:

  • 非直接耦合。模塊之間沒有直接的信息傳遞,稱爲非直接耦合。
  • 數據耦合。模塊之間通過接口傳遞參數(數據),稱爲數據耦合。
  • 標記耦合。模塊間通過接口傳遞內部數據結構的一部分(而不是簡單的參數),稱爲印記(Stamp)耦合。此數據結構的變化將使相關的模塊發生變化。
  • 控制耦合。模塊傳遞信號(如開關值、標誌量等)給另一個模塊,接收信號的模塊根據信號值調整動作,稱爲控制耦合。
  • 公共耦合。兩個以上的模塊共同引用一個全局數據項,稱爲公共耦合。
  • 內容耦合。當一個模塊直接修改或操作另一個模塊的數據,或者直接轉入另一個模塊時,就發生了內容耦合。

數據結構與算法設計

設計高效率的程序是基於良好的數據結構與算法,而不是基於編程小技巧。

一般說來,數據結構與算法就是一類數據的表示及其相關的操作(這裏算法不是指數值計算的算法)。從數據表示的觀點來看,存儲在數組中的一個有序整數表也是一種數據結構。算法是指對數據結構施加的一些操作,例如對一個線性表進行檢索、插入、刪除等操作。一個算法如果能在所要求的資源限制(Resource Constraints)範圍內將問題解決好,則稱這個算法是有效率(Efficient)的。例如一個資源限制可能是“用於存儲數據的內存有限”,或者“允許執行每個子任務所需的時間有限”。一個算法如果比其它已知算法所需要的資源都少,這個算法也被稱爲是有效率的。算法的代價(Cost)是指消耗的資源量。一般說來,代價是由一個關鍵資源例如時間或空間來評估的。

毋庸置疑,人們編寫程序是爲了解決問題。只有通過預先分析問題來確定必須達到的性能目標,纔有希望挑選出正確的數據結構。有相當多的程序員忽視了這一分析過程,而直接選用某一個他們習慣使用的,但是與問題不相稱的數據結構,結果設計出一個低效率的程序。如果使用簡單的設計就能夠達到性能目標時,選用複雜的數據結構也是沒有道理的。

人們對常用的數據結構與算法的研究已經相當透徹,可以歸納出一些設計原則:

1) 一種數據結構與算法都有其時間、空間的開銷和收益。當面臨一個新的設計問題時,設計者要徹底地掌握怎樣權衡時空開銷和算法有效性的方法。這就需要懂得算法分析的原理,而且還需要了解所使用的物理介質的特性(例如,數據存儲在磁盤上與存儲在內存中,就有不同的考慮)。

2) 開銷和收益有關的是時間——空間的權衡。通常可以用更大的時間開銷來換取空間的收益,反之亦然。時間——空間的權衡普遍地存在於軟件開發的各個階段中。

3) 設計人員應該充分地瞭解一些常用的數據結構與算法,避免不必要的重複設計工作。

4) 數據結構與算法爲應用服務。我們必須先了解應用的需求,再尋找或設計與實際應用相匹配的數據結構。

數據結構與算法設計的一般流程如下:

(1)數據結構與算法有全局和局部之分,當然先設計全局的,後設計局部的(通常在模塊設計時進行)。

(2)根據問題的特徵,先查找已經存在的數據結構與算法,挑選最合適的(並不一定是最先進的)。如果不存在現成的,那麼自己設計。

(3)設計並且編寫代碼之後,要進行測試。如果不滿足性能要求,那麼要進一步優化數據結構和算法。

1  系統設計的關鍵

軟件設計活動的關鍵又是什麼呢?還是讓我們回到現實世界去尋找答案吧!

在遠古時期,人類只能通過徒步從一個地方到達另一個地方。後來發現馬可以被馴服,通過馬車能更快地從一處到達另一處。再後來,人類逐步發明了自行車、汽車和飛機,且每一次發明都使得交通效率得以大幅提高。在這裏,馬車、自行車、汽車和飛機都共同地爲了解決交通效率問題。很顯然,馬車、自行車、汽車和飛機都是不同的概念,人類通過抽象發明這些概念,並通過相互學習的方式使得大家掌握其含義。如果將焦點放在交通工具上,我們會發現交通效率越高則其概念越抽象,複雜度也越高。或者說,越是抽象的概念,其所隱藏的複雜度就越高。

從這個關於交通的例子中我們不難發現,現實世界中我們是通過轉移複雜度的方式解決複雜問題的,且每一次不同的轉移都伴隨着抽象概念的建立,轉移的複雜度越多所建立的概念就越抽象。由於複雜度被轉移了後,使得一小部分人專注於被轉移的複雜度(比如製造飛機),而其他絕大部分人在享用複雜度被轉移所帶來好處的同時不用關心被轉移走的複雜度(乘飛機的人不用關心飛機的駕駛與製造)。同樣的事情也發生在軟件行業!不同的是軟件行業“製造”的不是汽車和飛機,而是編程語言、數據庫、平臺和框架等。

不難發現,軟件設計的關鍵活動應是抽象,通過抽象建立新的概念並將部分複雜度轉移到概念背後。儘管不斷的抽象增加了新概念的複雜度,但也簡化了所需解決問題的複雜度。顯然,好的設計應是其簡化的複雜度要大於因爲抽象所創造的複雜度。

2  系統設計的目標

系統設計的目標是在保證實現用戶功能和必要的性能的前提下,保證系統的質量特性達到規定的要求。

系統的功能、性能和質量特性必須通過設計來體現,通過設計方案的選擇來證明系統能夠滿足用戶在系統功能、性能和質量特性方面的用戶需求。

3  系統設計的依據

系統設計以需求規格說明書爲輸入,以產生滿足功能需求和非功能需求的設計方案爲輸出。

首先,待開發的軟件系統必須滿足功能需求。確切地講,首先必須滿足用戶功能需求,即軟件系統可以幫助用戶(包括系統所屬用戶和直接使用系統的用戶)解決用戶希望解決的問題。

其次,待開發的軟件系統必須滿足非功能需求。滿足非功能需求意味着軟件系統能夠像用戶所希望的那樣解決問題,並且維護方便。這裏,有三個方面的問題:

1、對約束的滿足。軟件系統能夠在用戶給定的環境下有效運行。約束是多方面的,包括硬件環境、軟件環境、社會環境(包括國家法律法規)、企業管理環境。當企業信息化進行到一定階段時,約束對軟件系統開發構成了不可逾越的障礙。

2、軟件的可用性。軟件的可用性除了軟件系統滿足約束外,還有軟件使用的方便性、可靠性、安全性等。--運行期質量

3、軟件的可維護性。軟件的可維護性不僅僅是軟件開發組織降低軟件維護成本的要求,實際上也是用戶的要求。當用戶需求發生變化後,用戶希望能夠很快地修改軟件系統而不影響對業務的處理。

開發架構設計關注軟件開發環境中軟件模塊的實際組織方式,即軟件系統由那些程序包組成,以及它們之間的關係。程序包不僅包括要編寫的源程序,而且包括直接使用的第三方SDK和現存的框架、類庫,支持系統運行的操作系統或中間件。

開發架構設計的重點考慮開發期質量,軟件模塊的組織有利於可擴展性、可重用性、可移植性、易理解性、易測試性等,設計中的關鍵技術主要體現軟件開發期質量有關的設計,如設計模式的運用,框架的選擇等。

開發架構設計的主要工作是:

確定要開發的程序包或直接利用的程序包(如函數庫)之間的依賴關係。

採用的技術,包之間採用什麼技術實現連接,以保證必要的開發期質量,如有依賴關係的包之間的鬆耦合。

確定採用的框架。如果實現連接的技術有對應的框架,採用框架是值得推薦的選擇。

分層模型開發架構設計的主要工具。三層架構模型把程序代碼分成負責與用戶交互的表現層(用戶界面層)代碼、負責業務處理的業務層代碼、負責數據服務的數據層代碼。表現層可以按用例分成若干個包,業務層可以可以按照業務邏輯獨立性分成若干個包,數據層可以由若干實體對象組成。其中,業務層由領域模型細化得到。由於領域模型相對穩定,所以業務層也相對穩定。表現層受表現設備和表現方式影響,數據層受數據庫系統影響,因此這兩層必須滿足可擴展性、可修改性等要求。

分層架構模式爲“把變化點封裝起來”提供了手段。分層架構的最大優點是將整體問題局部化,把可能的變化封裝到不同層中。最終將系統規劃爲單向依賴的分層體系,利於修改、擴展、替換。將代碼分爲層的好處是:層形成了開發小組的自然邊界—分層的開發人員需要的技巧是不同的。表現層的開發小組要深入瞭解用戶界面工具包的使用;數據層開發小組許傲熟悉相關的數據庫、數據持久化工具或文件系統。

採用分層和分區的描述方式能夠反映直接使用的程序包或框架的用法,使開發架構更清晰


附錄美文:http://www.csdn.net/article/2015-08-18/2825486

爲了給企業提供穩定可靠且優質的服務,作爲一名軟件架構師,在應用的架構設計上也是費盡心思,本文作者來自“風語者客服+”的CTO黃耀華,他從自己多年的實踐出發,總結了軟件架構設計的一些經驗,分享給大家。

“風語者客服+”是針對中小型企業推出的客服SaaS,節約了企業自建客服系統所需的巨大成本。爲了給企業提供穩定可靠且優質的服務,我們在整體架構上費盡心思。雖然不盡完美,希望藉此拋磚引玉,互相切磋。

前言

”Look deep into nature, and then you will understand everything better.“ -- Albert Einstein

我國傳統文化上,要做成一件事,講究三個方面:明道,優術,取勢。在軟件架構設計方面而言,也是類似的道理:遵循自然規律以明確大的方向,使用優秀的實操戰術,再根據實際情況落地。

這是個快餐年代,幾乎所有人都只做一件事 -“取勢”。 幾乎沒有多少人會去理解一個Servlet的工作原理,去理解一次HTTP請求的完整流程,因爲有超多框架幫你屏蔽了這裏的細節。詢問一個人會什麼技術,回答也往往是我會Hibernate、Spring、Ibatis、會PullToRefresh組件、會使用SDWebimage。不過這些框架(Framework)其實並不是軟件架構。軟件架構是一所有生命力的房子,而這些框架只是大一點的板磚。

因爲筆者水平有限,這裏只提一些普遍準則,也就是”正確的廢話“,以饗視聽。不會深入到實操戰術上,比如怎麼用Spring實施MVC架構,怎麼使用Maven管理依賴,Redis的常用操作,怎麼搭建一個負載均衡的集羣,如何使用阿里巴巴的Dubbo框架進行服務化等等。如果大家有興趣,可以自行搜索,有很多優秀的文章可供參考。

不幸的“程序猿”和“程序媛”各有各的痛苦,幸福的程序員都是相似的。其實說幸福有點言過其實,下面就說說怎麼讓他們不那麼痛苦。

一. 很好的模塊化支持

“At the bottom of every person's dependency, there is always pain, Discovering the pain and healing it is an essential step in ending dependency.” --Chris Prentiss

他們都在一個相對穩定的軟件架構裏編碼,自己的代碼不會依賴很多模塊,不會因爲自己微小的改動造成全局的失敗。正如"1984"中的老大哥說的,Ignorance is strength(”對外界的“無知就是一種力量).  任何一個模塊都不能有太強的存在感。

曾經在一個大型互聯網公司裏面,任何人只要用到一個核心模塊的功能,就必須依賴一個部署在某遠程服務器的庫,而且還有IP限制,只能把代碼部署到指定網段才能運行起來。導致基本上沒法在本地進行單元測試或者簡單調試。這個核心庫的存在感太強,就成了開發的瓶頸,嚴重的降低了生產力和碼農的幸福程度。

在“風語者客服+”的架構中,每個碼農都可以很方便的在本地把服務啓動起來,一分鐘up and running,隨便做一些改動就可以立竿見影的看到效果。這裏要歸功於幾個東西:

1. Git代碼管理

在團隊作戰中,每個程序員可以取下來完整的最新代碼庫,也可以在本地分支上盡情揮毫潑墨,而不擔心影響別人的工作。也可以把本地修改先stash起來,review一下別人的代碼,再unstash恢復回來。要想提高團隊效率,代碼倉庫管理建議儘快遷移到Git上。

2. Maven、Gradle、Cocopods等依賴管理

Maven是一個管理依賴(Dependency)的工具,現在在Java社區應該是比較普及的,無法想象現在還有團隊直接拷貝jar包來管理依賴。雖然早期沒有Maven的時候,都是拷貝jar包這麼過來的,碰到的問題也是顯而易見的,依賴的jar包作者改了某個bug,沒能及時傳導到調用方。多個調用方使用不一致的jar包,導致各種奇異bug。對應的在安卓社區,使用gradle的比較多,iOS的Objective-C開發中,多采用CocoaPods。

二. 高內聚,低耦合

He should focus on his knitting, not trying to do everything. Do one thing well.-- Steve Jobs

"Do one thing well"其實不算是老喬的專利,UNIX哲學和Google哲學都提倡這一點。這句話本身不完全對,比如對於一個商人,如果只會Do one thing well,那他無法在市場中存活,但是在工程師中卻是萬般推崇的哲學。

我們可以期望一個人具備一百種技能,然而對一個工具只期望它把一個需求解決好解決徹底,對於實現工具的一個類,一個方法,更是如此。但是,實際經驗中,我們經常看到一個5000行以上代碼的類,活像一個巨人版的瑞士軍刀,什麼都能做,但是什麼都做不好。這就是”Separation of Duty"沒有做好的典範。

在風語者”客服+“對外提供的SDK和API中,我們也提倡同樣的思想,力爭把App使用”客服+“SDK的門檻降到最低,每個API都能自言其一,而且API直接沒有時序上的依賴關係。內部各個模塊的開發,也秉承同樣的責任分割原則。責任分割原則的落實,沒有什麼好的框架或者工具來支持。只能通過老鳥經常去做Code Review,找出存在的問題,提出重構方案,並督促菜鳥改進。

個人一般採用的重構思路,僅作爲參考,照搬後被老闆批評乃至造成工傷概不負責:

  1. 把一個大的工具類,根據主題不同,拆分成若干個互不干擾的高內聚工具類;舉個例子,一個萬能的NetworkUtils可能可以拆成HttpUtils, FTPUtils,TelnetUtils等;
  2. 對於一個被頻繁調用的類,仔細觀察調用情況,如果有一些方法的被調用頻率遠遠低於其他方法,那麼需要考慮這個方法是不是應該放在這個類中;
  3. 存在A,B兩個類之間的相互依賴,或者更多類的混亂依賴,那麼就更要抽絲剝繭,通過合理安排類的功能來去除環形依賴;
  4. 嘗試一句話說清楚一個類的功能,不要使用“和”,“以及”,“或者”等連接詞;如果出現了這些連接詞,就需要引起重視;

三. 用進化擁抱變化

“It is not the strongest or the most intelligent who will survive but those who can best manage change.” ― Leon C. Megginson

前段時間,朋友圈瘋傳一篇文章 -——“架構腐化之謎”,大家都深表同感,紛紛表示對自己架構的未來的擔憂。然而,說句不合時宜的話, 90%的擔憂是杞人憂天,因爲以現在產品更新換代的速度,90%的項目面市即意味着死亡,沒等到架構腐朽,產品已經入土了。

剩下10%裏面,也許有9%會一直堅持活下去,但是不會蓬勃發展,也就是說,只要保證不出現內存泄露之類的問題,代碼就會一直在幾臺小服務器上運行下去,哪怕後面沒有人維護也沒關係。只有1%的產品,會日新月異的更新迭代,最終成長爲巨無霸,或者巨無霸的生態下的一個環節。這個言論看似悲觀,卻是對現實最好的妥協。

謬用一下泰戈爾的名言:“不是槌的打擊,而是水的載歌載舞,使鵝卵石臻於完美”, 不是閉門造車的架構,而是不斷擁抱變化的需求,才使得架構臻於完美。

假如在早期就糾結於架構的完美性,而延遲產品的交付,是非常得不償失的。只有生存下來,纔有機會。再根據市場變化,不斷優化架構,從而延長軟件的生命週期。那麼,假如撞大運,真的成了這1%,怎樣做才能算是擁抱變化?

首先,請參考本文第一點和第二點。如果這兩點基本功沒有練好,那麼談架構的進化就和還沒有通關十八羅漢的新手就想練成九陰真經是一個道理。

在設計之初,初步考慮系統的Scalability(可伸縮性)

下面在第四點會詳細闡述。

內部的各個模塊儘量做到可插拔

一方面是接口和實現的分離,可以隨着需求的變化更換實現;另一方面,儘量把功能服務化,成爲微服務,並且可以監控到服務的互相調用情況,當某個服務老化,可以逐步廢棄或使用新的服務取代之。這一點上,阿里巴巴的Dubbo框架是一個不錯的選擇。

儘量採用優秀的框架,站在巨人的肩膀上

例如在Web層面,我們使用Twitter的Bootstrap前端框架來實現響應式Web編程,提高生產效率的同時減少了爲解決各種設備適配問題的投入。當然,這就需要設計師配合,按照Bootstrap規範來設計頁面,減少一些個性化設計。

最後,考慮系統的Resilience(彈性,也叫耐受性)

俗一點說,就是變成一隻打不死的小強,代碼中儘量提前預判可能遇到的各種情形。經常看到代碼裏面有一堆的if(){}判斷語句,我就問作者,“你考慮過else{}嗎?”一般回答都是,“這絕對只有if,不會有else的”,可如果真的遇到else怎麼辦?千年蟲問題就是這麼誕生的。可能很多新同學還不知道什麼是千年蟲問題,簡單地說,就是當年的碼農,爲了省一點內存空間,只用了2位數來表達年份,比如int year = 98; 表達1998年。我猜碼農當時的心態也是,“就我這代碼,還能活到2000年,搞笑吧?”

程序員們平時可以多擴大自己的腦洞,想想有哪些else情況自己沒有處理,而且可以輕易處理的。比如服務器掛了,那麼App端是不是也要跟着crash,還是給出友好一點的提示,或者更友好一點,使用本地緩存。

四. 設計可擴展,但不要過度設計

it's better to have infinite scalability and not need it, than to need infinite scalability and not have it--@littleidea 網友

無限的擴展能力是一種奢望,但是起碼不能讓擴展能力成爲0。試想一下,你辛辛苦苦爲老闆開發了一個網站,過了一個月,網站超負荷了,老闆說,“小A啊,之前2臺服務器花了我5萬塊,預計流量馬上要翻倍了,再給你5萬塊,幫我扛過去啊。”結果你發現,問題不是線性增加服務器就能解決的,原來的程序沒有做分層(Web,Business Logic, Data Access等),導致加服務器也只能把所有層的代碼全搬到新的服務器,雖然只是Business Logic的計算有壓力,卻要浪費老闆很多服務器。更糟糕的是,因爲程序裏面用到了文件系統和操作系統命令,不好做負載均衡。

這裏有一些準則供參考:

  1. 代碼分層是必須的,層次明朗以後,當哪個層次的負載較重,想辦法對該層次進行優化或者擴容即可;
  2. 保持核心服務是無狀態的,所謂無狀態就是沒有和請求相關的數據依賴;
  3. 儘可能的選用已被驗證的廣泛採用的成熟基礎架構;
  4. 充分利用Zookeeper等集羣管理工具,來對服務進行管理;

風語者“客服+”中,把業務相關的代碼內部組裝爲風語者ServiceBox,使用阿里巴巴的Dubbo服務進行註冊管理。當負載增加時,可以迅速在運維層面增加服務節點,以提供更高的服務能力,從而保證客戶的優質體驗。


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