軟體中的質量屬性(一)
開發高質量的軟體是一件極具挑戰的工作。其中一個重要的原因就是對於“質量”的定義各不相同,變化莫測。
傑拉爾德溫伯格在他的四部曲鉅作《質量軟體管理》的第一卷第一章中就談到了什麼是質量以及質量的重要性。溫伯格在書中講了一個很有趣的故事。某軟體企業每年都會根據所開發軟體的質量對開發團隊進行獎勵,質量好的團隊將會獲得相應的嘉獎。可是如何評價軟體的質量是一個令人頭疼問題,於是他們採用了量化指標,根據使用者反饋的defect的數量來定。某款軟體質量超群,自推出至市場以來,只收到了一個defect。於是開發相應軟體的團隊獲得了年度質量大獎!可是這款軟體真的是一款高質量的軟體麼?看看這個defect的內容吧:“該軟體無法安裝!”
為了瞭解軟體的質量是否滿足要求,我們必須定義軟體的質量屬性(Quality Attributes)。同時質量屬性也是影響軟體架構的重要因素。軟體架構主要由需求決定,需求有功能性的和非功能性的,其中非功能性的需求主要就是指質量屬性,即各種“性”(“-ilities“)。wikipedia上列出了大概80種不同的質量屬性,下面我們討論一下其中最常見的屬性。
針對這些問題,為了增加可用性,需要考慮
糟糕的軟體中這三點往往是一起出現的。
軟體系統通常可以通過以分層,元件化等方式來提高靈活性。我在實踐中的原則是”用簡單構造複雜“。軟體系統本身是非常複雜的,然而構建軟體系統的基本單元卻應該是非常簡單的。例如計算機的基本組成單元是閘電路,每一個閘電路都非常簡單,然而計算機系統卻是如此的複雜和靈活。凱文凱利在他的《
構建靈活軟體系統的關鍵在於找到那個簡單單元的邊界,每一個單元應該足夠的簡單,但是不能夠過於簡單,愛因斯坦說過“Simple,but not Simpler!”
我們可以看出,這些問題大多是管理的問題。我個人認為,為了實現概念一致性,在軟體的設計過程中應該儘可能少的引入新的概念。軟體設計的過程是一個抽象的過程,我們把複雜的軟體系統抽象為一個個的層,問題域,過程,模組,服務,介面,這些都是非常必要的。但是這些東西都應該是越少越好,能在一兩層解決的問題,絕不要劃分成四五層,能提供一個API介面,絕不要給三個。人類能夠同時掌握的概念是有限的,大部分人可能也就三四個把,當你設計的系統中有五六個或者七八個陌生的概念需要同時掌握的時候,對你的團隊中其它需要使用你的設計的開發人員來說絕對是一個巨大的挑戰。大部分人不會費力氣去搞懂你設計的高大上的新概念。他們很有可能會設計出一套對他們自己更容易理解的並行的方案來解決同樣的問題。
-
可用性(Availability)
可用性是指系統正常工作的時間所佔的比例。可用性會遇到系統錯誤,惡意攻擊,高負載等問題的影響。
當年在一家儲存公司開發管理軟體,"HA"(High Availability)是一個經常被提及的效能屬性。"DU"(Data Unavailable)和"DL"(Data Lost)都是非常嚴重的可用性問題。
可用性面臨的主要問題有:
-
物理層失效:比如資料庫伺服器宕機,停電, 網路欠費被中國電信斷網
-
惡意攻擊:例如DOS(Deny of Service)攻擊
-
軟體的設計問題或BUG:比如錯誤的資源控制鎖導致某個資源長期被佔用,各種core dump, out of memery, out of stack。
-
升級或日常維護
-
如何設計故障轉移(failover),一般可以使用冗餘來消除系統中的單點故障。可以是各種冷熱備份,分散式系統。
-
如何設計線上升級。我當年在那家儲存公司的一個主要責任就是做線上升級,因為儲存裝置有兩個同時工作的單元構成,所以升級的過程簡單說就是先升級第一個單元,然後再升級第二個單元。聽上去非常簡單,然而實際的升級過程非常複雜,在升級之前,會做非常多的健康檢查,比如檢查兩個單元是不是都在正常工作,有沒有壞的磁碟,儲存裝置的版本是不是滿足要求,等等等等。特別是由於儲存裝置的軟硬體型號複雜,還要考慮各種不同的執行環境,各種軟體BUG,健康檢查非常非常的複雜。當然大多數情況下,使用者可以跳過健康檢查,然而由此引發的升級失敗,後果自負!
-
如何設計異常處理。異常處理是一個很大的話題,為了支援高可用性,我們應該如何處理異常呢?舉個例子,一個網站要處理使用者的訂單,然而由於資料庫伺服器的故障,雖然前端的服務一切正常,可是訂單無法處理。你會怎麼處理這個資料庫伺服器異常的情況呢?大多數的程式設計師會在捕獲異常的時候寫日誌,把異常狀記錄下來,然後在客戶端的UI上顯示一段誰也看不懂的異常程式碼。這樣的異常處理其實跟沒做差不多,甚至更糟。那麼把異常程式碼翻譯成使用者可以看懂的語言是不是會好一點呢?也許會,如果你告訴使用者因為你的資料庫故障,請明天再來,可想而知使用者會多麼失望!好的異常處理是把使用者的訂單請求記錄下來,發給人工處理,或者等待資料庫恢復後自動處理,並告訴使用者訂單已經處理,並有可能遲延,請求得到使用者的諒解。
-
如何應對不穩定的網路連線。
-
-
靈活性 (Flexibility)
靈活性是指系統是否能夠很容易的適應環境和需求的變化。
例如現在需求是返回10以內的所有質數。我們可以使用以下程式:
function prime(){ var result = [2,3,5,7]; return result; }
這段程式非常好,效能也非常高。然而非常的不具備靈活性,通過對需求的分析我們似乎可以大膽的預見10是一個非常有可能會改變的需求,於是提高靈活性的方式就是把10變成可變引數:
function prime(range){ var result = []; var i,k; for(i=2; i<=range; i++){ result.push(i); } for(i=0; i<result.length; i++){ for(k=i+1; k<result.length; k++){ if(result[k]%result[i]==0){ result.splice(k,1); } } } return result; }
我們看到第二段程式碼為了增加靈活性,程式碼變的更復雜,執行時間變長。當然第一段程式碼中的質數根本就沒有經過計算驗證,完全是我自己計算出來的,因為10以內的質數這樣簡單的運算根本不需要計算機。
在軟體開發中有哪些問題會引起靈活性下降呢?
-
由於各種原因而造成的數量驚人的程式碼
-
過於複雜的程式碼
-
不斷重複的程式碼
-
-
概念一致性 (Conceptual Integrity)
我們很少在軟體設計的時候談論概念一致性,也許我們認為概念一致是一個共同的假定,然而實際上,在軟體開發的過程中,往往會出現很多概念不一致的情況。
概念一致性的問題主要表現在以下的方面:
-
在一個模組的設計中混雜不同的問題域
-
不同的組織或者團隊負責系統中的同一個功能
-
沒有統一的程式碼規範
-
為了滿足後向相容,系統中存在新舊兩套不同的程式碼棧
-
在以後我們將接著討論軟體中其它主要的質量屬性。
當我們設計軟體的時候,需要定義哪些質量屬性是我們希望實現的,切記,質量屬性並非越多越好。一般來說找到最重要的三個來構建軟體就好了,而且"魚和熊掌不能得兼",各個質量屬性之間有可能是互相矛盾或者互相影響的。分散式資料庫中的CAP理論就是一個典型的例子。對於一個分散式的計算系統可不能同時滿足一致性(Consistency),可用性(Availability),分割槽容忍性(Partition Tolerance)。