第6章:可維護性軟件構建方法 6.1可維護性的度量和構造原則

大綱

軟件維護和演變
可維護性度量
模塊化設計和模塊化原則
OO設計原則:SOLID
OO設計原則:GRASP
總結程序員

軟件維護和演變

什麼是軟件維護?編程

軟件工程中的軟件維護是交付後修改軟件產品以糾正故障,提升性能或其餘屬性。軟件維護:修復錯誤,改善性能
在「ISO / IEC 14764:2006軟件工程 - 軟件生命週期過程 - 維護」設計模式

運維工程師安全

維護是軟件生產中最困難的方面之一,由於維護包含了全部其餘階段的各個方面
用戶報告故障並由維護工程師處理。網絡

維護工程師必須具有出色的調試技能app

  • 故障可能存在於產品中的任何地方,故障的緣由可能在於如今不存在的規格或設計文檔(錯誤/問題本地化)。
  • 須要高超的診斷技能,測試技能和文檔技能(測試,修復和記錄更改)。

軟件維護的類型運維

糾正性維修25%糾錯性編輯器

  • 對交付後執行的軟件產品進行無功修改,以糾正發現的問題;

適應性維護21%適應性模塊化

  • 修改交付後執行的軟件產品,以保持軟件產品在變化或變化的環境中可用;

完善性維護50%完善性函數

  • 交付後加強軟件產品以提升性能或可維護性;

預防性維護4%預防性

  • 交付後對軟件產品進行修改,以便在軟件產品發生有效故障前檢測並糾正軟件產品中的潛在故障。

雷曼關於軟件演化的規律(Lehman’s Laws on Software Evolution)

反饋系統
持續變化
持續增加
質量降低
增長複雜性
自我調節

  • 保持組織穩定
  • 保持熟悉

軟件維護和進化的目標

軟件維護和演化的目標:爲了提升軟件的適應性和適應性,並保持其活力,即「長期軟件(低熵軟件)」
提升軟件的適應性,延續軟件生命
Linux內核發展的一個例子:可維護性指數

維護不只僅是op工程師的任務......

維護不只僅是維護和操做工程師的任務,也是軟件設計人員和開發人員的潛在任務。 軟件維護不只僅是運維工程師的工做,而是從設計和開發階段就開始了
對他們來講,在設計和施工階段必須考慮軟件的將來潛在變化/擴展;在設計與開發階段就要考慮未來的可維護性
所以,靈活和可擴展的設計/結構被全面考慮,換句話說,「易於改變/擴展」。 設計方案的「容易改變」
這就是所謂的軟件構建的「可維護性」,「可擴展性」和「靈活性」。

可維護性建設的例子

模塊化設計與實現模塊化

  • 低耦合和高內聚力

OO設計原則OO設計原則

  • SOLID,GRASP

OO設計模式OO設計模式

  • 工廠方法模式,Builder模式
  • 橋模式,代理模式
  • 記念模式,狀態模式

基於狀態的構造技術(自動機編程)
表驅動的構造技術
基於語法的構造技術(Grammar-based construction)

可維護性度量

許多可維護性的名字

可維護性

  • 「軟件系統或組件可輕鬆修改以糾正故障,改善性能或其餘屬性,或適應變化的環境」。

可擴展性

  • 軟件設計/實現將將來的增加考慮在內,並被視爲擴展系統能力的系統性測量以及實現擴展所需的工做量。

靈活性

  • 軟件根據用戶需求,外部技術和社會環境等容易改變的能力。

可適應性

  • 一種交互式系統(自適應系統)的能力,能夠根據所得到的有關其用戶及其環境的信息,使其行爲適應我的用戶。

可管理性

  • 如何有效和輕鬆地監控和維護軟件系統,以保持系統的正常運行,安全並平穩運行。

可支持性

  • 基於包含質量文檔,診斷信息以及知識豐富且可用的技術人員的資源,能夠有效地使軟件在部署後保持運行。

有關可維護性的問題

Code review的時候常常問的關於可維護性的問題:

  • 結構和設計簡單:改變事物有多容易?
  • 事情緊密或鬆散(即關注點分離)?
  • 包/模塊中的全部元素是否具備凝聚力,其職責是否清晰且密切相關?
  • 它是否具備過深的繼承層次結構,仍是同意繼承的組合?
  • 方法定義中存在多少個獨立的執行路徑(即,cycolmatic複雜度)?
  • 存在多少代碼重複?

一些經常使用的可維護性度量標準

圈複雜度 - 測量代碼的結構複雜度。

  • 它是經過計算程序流程中不一樣代碼路徑的數量而建立的。
  • 具備複雜控制流程的程序將須要更多的測試來實現良好的代碼覆蓋率,而且將不易維護。
  • CC = E-N + 2,CC = P + 1,CC =區域的數量

Lines of Code(代碼行數) - 表示代碼中的近似行數。

  • 很是高的數值可能代表某種類型或方法試圖作太多工做,應該分手。
  • 這也可能代表類型或方法可能難以維護。

對於給定的問題,令:
η_1=不一樣運算符的數目
η_2=不一樣的操做數的數量
Ν_1=運算符總數
Ν_2=操做數總數
從這些數字中能夠計算出幾項度量:
程序詞彙表:η=η_1+η_2
程序長度:N=Ν_1+Ν_2
計算的程序長度:N ̂=η_1 log_2⁡〖η_1 〗+η_2 log_2⁡〖η_2 〗
體積:V=N×log_2⁡η
難度:D=η_1/2×Ν_2/η_2
代價:E=D×V
難度測量與程序編寫或理解的難度有關
代價度量使用如下關係轉化爲實際編碼時間,
編程所需的時間:T=E/18 秒
Halstead提供的錯誤(B)是對執行錯誤數量的估計。
提供的錯誤數量:B = E^(2/3)/3000或更接近的B = V/3000也被接受。
Halstead Volume:基於源代碼中(不一樣)運算符和操做數的數量的合成度量。

一些經常使用的可維護性度量標準

可維護性指數(MI)- 計算介於0和100之間的索引值,表示維護代碼的相對容易性。 高價值意味着更好的可維護性。 它的計算基於:

  • Halstead體積(HV)
  • 圈複雜性(CC)
  • 每一個模塊的平均代碼行數(LOC)
  • 每一個模塊註釋行的百分比(COM)。

一些經常使用的可維護性度量標準

繼承的層次數

  • 表示擴展到類層次結構的根的類定義的數量。 等級越深,就越難理解特定方法和字段在何處被定義或從新定義。

類之間的耦合度

  • 經過參數,局部變量,返回類型,方法調用,泛型或模板實例化,基類,接口實現,在外部類型上定義的字段和屬性修飾來測量耦合到惟一類。
  • 良好的軟件設計決定了類型和方法應該具備高內聚性和低耦合性。
  • 高耦合表示一種設計難以重用和維護,由於它與其餘類型之間存在許多相互依賴關係。

單元測試覆蓋率

  • 指示代碼庫的哪些部分被自動化單元測試覆蓋。 (將在第7章中研究)

模塊化設計和模塊化原則

模塊化編程

模塊化編程是一種強調將程序功能分離爲獨立,可互換模塊的設計技術,每種模塊都包含執行所需功能一個方面所需的一切。
將整個程序的代碼高度分解爲結構化編程和OOP。

模塊化編程設計的目標是將系統劃分爲模塊,並經過如下方式在組件之間分配責任:

  • 模塊內高凝聚力(高內聚)
  • 模塊之間的耦合鬆耦合(低耦合)

模塊化下降了程序員在任什麼時候候都必須處理的整體複雜性,假設:

  • 將功能分配給將類似功能組合在一塊兒的模塊(分離關注點)
  • 模塊之間有小而簡單的定義明確的接口(信息隱藏)

內聚和耦合的原則多是評估設計可維護性的最重要的設計原則。

(1)評估模塊性的五個標準

Decomposability(可分解性)

  • 較大的組件是否分解爲較小的組件?

Composability(可組合性)

  • 較大的組件是否由較小的組件組成?

Understandability(可理解性)

  • 組件是否可單獨理解

Continuity(可持續性)

  • 規範的小改動是否會影響本地化和有限數量的組件?

Protection(出現異常以後的保護)

  • 運行時異常的影響是否侷限於少數相關組件?

(2)模塊化設計的五條原則

Direct Mapping (直接映射)
Few Interfaces (儘量少的接口)
Small Interfaces (儘量小的接口)
Explicit Interfaces (顯式接口)
Information Hiding (信息隱藏)

(3)耦合和內聚

耦合

耦合是模塊之間依賴關係的度量。 若是兩個模塊之間的變化可能須要另外一個模塊的變動,則兩個模塊之間存在依賴關係。

模塊之間的耦合度取決於:

  • 模塊之間的接口數量(數量)和
  • 每一個接口的複雜性(由通訊類型決定)(質量)

HTML,CSS和JavaScript之間的耦合

一個精心設計的網絡應用程序模塊化:

  • 指定數據和語義的HTML文件
  • 規定HTML數據的外觀和格式的CSS規則
  • 定義頁面行爲/交互性的JavaScript

內聚

內聚是衡量一個模塊的功能或責任有多強烈程度的一個指標。
若是一個模塊的全部元素都朝着相同的目標努力,那麼它就具備很高的內聚。

最好的設計在模塊內具備高內聚力(也稱爲強內聚力)和模塊之間的低耦合(也稱爲弱耦合)。

OO設計原則:SOLID

SOLID:5類設計原則
(SRP) The Single Responsibility Principle 單一責任原則
(OCP) The Open-Closed Principle 開放-封閉原則
(LSP) The Liskov Substitution Principle Liskov替換原則
(DIP) The Dependency Inversion Principle 依賴轉置原則
(ISP) The Interface Segregation Principle 接口聚合原則

(1)單一責任原則(SRP)

「類改變不該該有一個以上的緣由」,即一個類應該集中精力作一件事,只能一件事。

責任:「變動的理由」(責任:變化的緣由)
SRP:

  • 類改變不該該有一個以上的緣由。 (不該有多於1個的緣由使得一個類發生變化)
  • 一個類,一個責任。(一個類,一個責任)

若是一個類包含了多個責任,那麼將引發不良後果:

  • 引入額外的包,佔據資源
  • 致使頻繁的從新配置,部署等

SRP是原則中最簡單的一種,也是最難作到的一種。(最簡單的原則,倒是最難作好的原則)

(2)開放/封閉原則(OCP)

類應擴展(對擴展性的開放)

  • 這意味着模塊的行爲能夠擴展。 咱們能夠根據應用程序的需求變化,或者爲了知足新應用程序的需求,使模塊以新的和不一樣的方式運行。 (模塊的行爲應是可擴展的,從而該模塊可表現出新的行爲以知足需求的變化)

但關閉修改。(對修改的封閉)

  • 這種模塊的源代碼是不可侵犯的。 沒有人能夠對其進行源代碼更改。(但模塊自身的代碼是不該被修改的)
  • 擴展模塊行爲的正常方法是對該模塊進行更改。(擴展模塊行爲的通常途徑是修改模塊的內部實現)
  • 沒法更改的模塊一般被認爲具備固定的行爲。(若是一個模塊不能被修改,那麼它一般被認爲是具備固定的行爲)

Key:abstraction(關鍵的解決方案:抽象技術)
「軟件實體(類,模塊,函數等)應該被打開以進行擴展,可是爲了修改而關閉」,即,使用繼承和組合來改變類的行爲

開放封閉原則 - 幾個問題...。
不可能在不修改GraphEditor的狀況下添加新的Shape
重要的是要了解GraphEditor添加一個新的形狀
GraphEditor和Shape之間的緊密耦合
不參與GraphEditor就很難測試特定的Shape
If-Else-/Case應該被避免

OCP表示單一選擇
只要軟件系統必須支持一組替代方案,系統中的一個且只有一個模塊應該知道他們的詳盡列表。
編輯器:一組命令(插入,刪除等)
圖形系統:圖形類型集(矩形,圓形等)
編譯器:語言結構集(指令,循環,表達式等)

(3)Liskov替換原則(LSP)

「使用指針或對基類的引用的函數必須可以使用派生類的對象而不知道它」,即,子類在使用它們的基類時應該表現得很好

LSP:子類型必須可替代其基本類型。(子類型必須可以替換其基類型)

派生類必須能夠經過基類接口使用,而不須要客戶端了解其差別。 (派生類必須可以經過其基類的接口使用,客戶端無需瞭解兩者之間的差別)

在第5-2節中已經討論過可複用性。

(4)接口隔離原理(ISP)

「客戶不該該被迫依賴他們不使用的接口」,即保持接口小。
不要強制類來實現它們不能實現的方法(Swing / Java)
不要用不少方法污染界面
避免「胖」的接口

客戶不該該被迫依賴他們不使用的方法。(客戶端不該依賴於它們不須要的方法)
接口屬於客戶端,而不屬於層次結構。
這個原則處理「胖」接口的缺點。(「胖」接口具備不少缺點)
具備「胖」接口的類是接口不具備內聚性的類。(不夠聚合)

  • 該類的接口能夠分解爲多組成員函數。(胖接口可分解爲多個小的接口)
  • 每一個小組服務一組不一樣的客戶端(不一樣的接口向不一樣的客戶端提供服務)。
  • 所以有些客戶使用一組成員函數,而其餘客戶使用其餘組。(客戶端只訪問本身所須要的端口)

(5)依賴倒置原理(DIP)

高級模塊不該該依賴於低級模塊。 二者都應該取決於抽象。

  • 抽象不該該依賴於細節(抽象的模塊不該依賴於具體的模塊)
  • 細節應該取決於抽象(具體應依賴於抽象)

應該使用大量的接口和抽象!

爲何DIP?

優勢:

  • 將類契約正式化。
  • 您根據前置和後置條件定義例程的服務。 這很是清楚會發生什麼。

嘗試設計測試

  • 建立一個測試友好的設計
  • 測試友好的模塊可能會展示其餘重要的設計特徵。

例如:你會避免循環依賴。 若是您必須從UI單獨測試,業務邏輯將更好地與UI代碼隔離

OO設計原則:GRASP

什麼是GRASP模式

通常責任分配軟件模式(原則),縮寫爲GRASP,包含爲OOP中的類和對象分配責任的準則。
GRASP模式是幫助理解基本對象設計的學習輔助,並以有條理,合理,可解釋的方式應用設計推理。
這種理解和使用設計原則的方法是基於對班級分配責任的模式。
GRASP是關於如何爲「類」和「對象」指派「職責」的一系列原則

什麼是責任

對象的責任:與對象的義務有關

瞭解:

  • 瞭解私有封裝數據
  • 瞭解相關對象
  • 瞭解它能夠派生或計算的事物

這樣作:

  • 本身作一些事情,好比建立一個對象或者作一個計算
  • 在其餘對象中發起行動
  • 控制和協調其餘對象的活動。

總結

軟件維護和演變可維護性度量模塊化設計和模塊化原則OO設計原則:SOLIDOO設計原則:GRASP

相關文章
相關標籤/搜索