【OOAD】面向對象設計原則概述

  • 軟件的可維護性和可複用性

知名軟件大師Robert C.Martin認爲一個可維護性(Maintainability) 較低的軟件設計,一般因爲以下4個緣由形成:
 過於僵硬(Rigidity)
 過於脆弱(Fragility)
 複用率低(Immobility)
 黏度太高(Viscosity)
程序員

軟件工程和建模大師Peter Coad認爲,一個好的系統設計應該具有以下三個性質:
 可擴展性(Extensibility)
 靈活性(Flexibility)
 可插入性(Pluggability)編程

軟件的複用(Reuse)或重用擁有衆多優勢,如能夠提升軟件的開發效率,提升軟件質量,節約開發成本,恰當的複用還能夠改善系統的可維護性。
面向對象設計複用的目標在於實現支持可維護性的複用。
在面向對象的設計裏面,可維護性複用都是以面向對象設計原則爲基礎的,這些設計原則首先都是複用的原則,遵循這些設計原則能夠有效地提升系統的複用性,同時提升系統的可維護性。

面向對象設計原則和設計模式也是對系統進行合理重構的指南針,重構(Refactoring)是在不改變軟件現有功能的基礎上,經過調整程序代碼改善軟件的質量、性能,使其程序的設計模式和架構更趨合理,提升軟件的擴展性和維護性。設計模式

  • 面向對象設計原則簡介

經常使用的面向對象設計原則包括7個,這些原則並非孤立存在的,它們相互依賴,相互補充。
架構

  • 單一職責原則

單一職責原則定義
單一職責原則(SRP),就一個類而言,應該僅有一個引發它變化的緣由。
Each class should have one and only one reason to change.併發

若是一個類承擔的職責過多,就等於把這些職責耦合在一塊兒,
一個職責的變化可能會削弱或者抑制這個類完成其餘職責的能力。
這種耦合會致使脆弱的設計,當變化發生時,設計會遭受到意想不到的破壞。編程語言

單一職責原則(Single Responsibility Principle, SRP)定義以下:
一個對象應該只包含單一的職責,而且該職責被完整地封裝在一個類中。
其英文定義爲:
Every object should have a single responsibility, and that responsibility should be entirely encapsulated by the class.
另外一種定義方式以下:
就一個類而言,應該僅有一個引發它變化的緣由。
其英文定義爲:
There should never be more than one reason for a class to change.函數

單一職責原則分析
一個類(或者大到模塊,小到方法)承擔的職責越多,它被複用的可能性越小,並且若是一個類承擔的職責過多,就至關於將這些職責耦合在一塊兒,當其中一個職責變化時,可能會影響其餘職責的運做。
類的職責主要包括兩個方面:數據職責和行爲職責,數據職責經過其屬性來體現,而行爲職責經過其方法來體現。
單一職責原則是實現高內聚、低耦合的指導方針,在不少代碼重構手法中都能找到它的存在,它是最簡單但又最難運用的原則,須要設計人員發現類的不一樣職責並將其分離,而發現類的多重職責須要設計人員具備較強的分析設計能力和相關重構經驗。性能

手機職責過多嗎?
吃西餐:刀叉,分工很明晰,刀就是切割食物,叉就是固定食物或者移動食物;測試

  • 開閉原則

開閉原則定義
對於擴展是開放的(Open for extension),
對於更改是封閉的(Closed for modification)優化

開閉原則(Open-Closed Principle, OCP)定義以下:
一個軟件實體應當對擴展開放,對修改關閉。也就是說在設計一個模塊的時候,應當使這個模塊能夠在不被修改的前提下被擴展,即實如今不修改源代碼的狀況下改變這個模塊的行爲。
其英文定義爲:
Software entities should be open for extension, but closed for modification.

說的更通俗點兒,就是說咱們開發了一個軟件,應該能夠對它進行功能擴展(開放),而在進行這些擴展的時候,不須要對原來的程序進行修改(關閉)!

Bertrand Meyer是ETH Zurich的軟件工程教授,也是Eiffel軟件的首席架構師,他領導並設計了EiffelStudio環境和大量的庫。他是一些暢銷書的做者,其中包括得到Jolt大獎的「Object-Oriented Software Construction (Prentice Hall)」。他也由於在對象技術和Eiffel方面的工做得到了ACM軟件系統大獎和Dahl-Nygaard大獎,並得到了St. Petersburg州立技術大學的榮譽博士學位。他的研究興趣涉及面向對象技術、編程語言、軟件驗證(包括測試、併發和規範方法)。他也是一名活躍的顧問和講師。

開閉原則分析
開閉原則由Bertrand Meyer於1988年提出,它是面向對象設計中最重要的原則之一。
在開閉原則的定義中,軟件實體能夠指一個軟件模塊、一個由多個類組成的局部結構或一個獨立的類。
抽象化是開閉原則的關鍵。
開閉原則還能夠經過一個更加具體的「對可變性封裝原則」來描述,對可變性封裝原則(Principle of Encapsulation of Variation, EVP)要求找到系統的可變因素並將其封裝起來。

  • 里氏代換原則

里氏代換原則定義
里氏代換原則(Liskov Substitution Principle, LSP)有兩種定義方式,第一種定義方式相對嚴格,其定義以下:
若是對每個類型爲S的對象o1,都有類型爲T的對象o2,使得以T定義的全部程序P在全部的對象o1都代換成o2時,程序P的行爲沒有變化,那麼類型S是類型T的子類型。
其英文定義爲:
If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.
第二種更容易理解的定義方式以下:
全部引用基類(父類)的地方必須能透明地使用其子類的對象。
其英文定義爲:
Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.
里氏代換原則分析

里氏代換原則由2008年圖靈獎得主、美國第一位計算機科學女博士、麻省理工學院教授Barbara Liskov和卡內基.梅隆大學Jeannette Wing教授於1994年提出。其原文以下:Let q(x) be a property provable about objects x of type T. Then q(y) should be true for objects y of type S where S is a subtype of T.
里氏代換原則能夠通俗表述爲:在軟件中若是可以使用基類對象,那麼必定可以使用其子類對象。把基類都替換成它的子類,程序將不會產生任何錯誤和異常,反過來則不成立,若是一個軟件實體使用的是一個子類的話,那麼它不必定可以使用基類。
里氏代換原則是實現開閉原則的重要方式之一,因爲使用基類對象的地方均可以使用子類對象,所以在程序中儘可能使用基類類型來對對象進行定義,而在運行時再肯定其子類類型,用子類對象來替換父類對象。

芭芭拉•利斯科夫(Barbara Liskov),美國計算機科學家,2008年圖靈獎得主,2004年約翰.馮諾依曼獎得主,美國工程院院士,美國藝術與科學院院士,美國計算機協會會士。現任麻省理工學院電子電氣與計算機科學系教授。她是美國第一個計算機科學女博士。
周以真(Jeannette M. Wing),美國計算機科學家,卡內基.梅隆大學教授,美國國家天然基金會計算與信息科學工程部助理部長,ACM和IEEE會士。
Barbara Liskov,2008年度美國計算機學會(ACM)圖靈獎(Turing Award)得到者,美國第一個得到計算機科學博士學位的女性(1968年,斯坦福大學),其創新性研究給計算機編程領域帶來了巨大變革。它的提出甚至要早於OCP。不過遺憾的是,因爲對這一原則的理解各不相同,通過屢次的翻譯、轉述,LSP成了OOD設計原則中爭議最多的話題之一。其實早在1987年的OOPSLA大會上,麻省理工學院(MIT)計算機科學實驗室的Liskov女士就發表了經典文章Data Abstraction and Hierarchy,其中提出了以她名字命名的Liskov替換原則(The Liskov Substitution Principle),簡稱LSP。該原則說明了何時該使用繼承,何時不應使用以及爲何。

里氏替換法包含了四層意思:
子類必須徹底的實現父類的方法;
子類能夠有本身的個性;
覆蓋或實現父類的方法時輸入參數能夠被放大;
覆蓋或實現父類的方法是輸出結果能夠被縮小。

  • 依賴倒轉原則

依賴倒轉原則定義
依賴倒轉原則(Dependence Inversion Principle, DIP)的定義以下:
高層模塊不該該依賴低層模塊,它們都應該依賴抽象。抽象不該該依賴於細節,細節應該依賴於抽象。
其英文定義爲:
High level modules should not depend upon low level modules, both should depend upon abstractions. Abstractions should not depend upon details, details should depend upon abstractions.
另外一種表述爲:
要針對接口編程,不要針對實現編程。
其英文定義爲:
Program to an interface, not an implementation.
依賴倒轉原則分析
依賴倒轉原則是Robert C. Martin在1996年爲《C++ Reporter》所寫的專欄Engineering Notebook的第三篇,後來加入到他在2002年出版的經典著做《Agile Software Development, Principles, Patterns, and Practices》中。
簡單來講,依賴倒轉原則就是指:代碼要依賴於抽象的類,而不要依賴於具體的類;要針對接口或抽象類編程,而不是針對具體類編程。
實現開閉原則的關鍵是抽象化,而且從抽象化導出具體化實現,若是說開閉原則是面向對象設計的目標的話,那麼依賴倒轉原則就是面向對象設計的主要手段。
依賴倒轉原則的經常使用實現方式之一是在代碼中使用抽象類,而將具體類放在配置文件中。
「將抽象放進代碼,將細節放進元數據」
Put Abstractions in Code, Details in Metadata
(《程序員修煉之道:從小工到專家》(The Pragmatic programmer: from journeyman to master) )

類之間的耦合
 零耦合關係
 具體耦合關係
 抽象耦合關係
依賴倒轉原則要求客戶端依賴於抽象耦合,以抽象方式耦合是依賴倒轉原則的關鍵。

依賴注入
構造注入(Constructor Injection):經過構造函數注入實例變量。
設值注入(Setter Injection):經過Setter方法注入實例變量。
接口注入(Interface Injection):經過接口方法注入實例變量。

  • 接口隔離原則

接口隔離原則定義
接口隔離原則(Interface Segregation Principle, ISP)的定義以下:
客戶端不該該依賴那些它不須要的接口。
其英文定義爲:
Clients should not be forced to depend upon interfaces that they do not use.
注意,在該定義中的接口指的是所定義的方法。
另外一種定義方法以下:
一旦一個接口太大,則須要將它分割成一些更細小的接口,使用該接口的客戶端僅需知道與之相關的方法便可。
其英文定義爲:
Once an interface has gotten too 'fat' it needs to be split into smaller and more specific interfaces so that any clients of the interface will only know about the methods that pertain to them.
接口隔離原則分析
Robert C. Martin在1996年提出
接口隔離原則是指使用多個專門的接口,而不使用單一的總接口。每個接口應該承擔一種相對獨立的角色,很少很多,不幹不應乾的事,該乾的事都要幹。
(1) 一個接口就只表明一個角色,每一個角色都有它特定的一個接口,此時這個原則能夠叫作「角色隔離原則」。
(2) 接口僅僅提供客戶端須要的行爲,即所需的方法,客戶端不須要的行爲則隱藏起來,應當爲客戶端提供儘量小的單獨的接口,而不要提供大的總接口。

  • 合成複用原則

合成複用原則定義
合成複用原則(Composite Reuse Principle, CRP)又稱爲組合/聚合複用原則(Composition/ Aggregate Reuse Principle, CARP),其定義以下:
儘可能使用對象組合,而不是繼承來達到複用的目的。
其英文定義爲:
Favor composition of objects over inheritance as a reuse mechanism.
合成複用原則分析
合成複用原則就是指在一個新的對象裏經過關聯關係(包括組合關係和聚合關係)來使用一些已有的對象,使之成爲新對象的一部分;新對象經過委派調用已有對象的方法達到複用其已有功能的目的。簡言之:要儘可能使用組合/聚合關係,少用繼承。
在面向對象設計中,能夠經過兩種基本方法在不一樣的環境中複用已有的設計和實現,即經過組合/聚合關係或經過繼承。
繼承複用:實現簡單,易於擴展。破壞系統的封裝性;從基類繼承而來的實現是靜態的,不可能在運行時發生改變,沒有足夠的靈活性;只能在有限的環境中使用。(「白箱」複用 )
組合/聚合複用:耦合度相對較低,選擇性地調用成員對象的操做;能夠在運行時動態進行。(「黑箱」複用 )

組合/聚合可使系統更加靈活,類與類之間的耦合度下降,一個類的變化對其餘類形成的影響相對較少,所以通常首選使用組合/聚合來實現複用;其次才考慮繼承,在使用繼承時,須要嚴格遵循里氏代換原則,有效使用繼承會有助於對問題的理解,下降複雜度,而濫用繼承反而會增長系統構建和維護的難度以及系統的複雜度,所以須要慎重使用繼承複用。

  • 迪米特法則

迪米特法則定義
一個軟件實體應當儘量少的與其餘實體發生相互做用
迪米特法則(Law of Demeter, LoD)又稱爲最少知識原則(Least Knowledge Principle, LKP),它有多種定義方法,其中幾種典型定義以下:
(1) 不要和「陌生人」說話。英文定義爲:Don't talk to strangers.
(2) 只與你的直接朋友通訊。英文定義爲:Talk only to your immediate friends.
(3) 每個軟件單位對其餘的單位都只有最少的知識,並且侷限於那些與本單位密切相關的軟件單位。英文定義爲:Each unit should have only limited knowledge about other units: only units "closely" related to the current unit.
迪米特法則分析
迪米特法則來自於1987年秋美國東北大學(Northeastern University)一個名爲「Demeter」的研究項目。
簡單地說,迪米特法則就是指一個軟件實體應當儘量少的與其餘實體發生相互做用。這樣,當一個模塊修改時,就會盡可能少的影響其餘的模塊,擴展會相對容易,這是對軟件實體之間通訊的限制,它要求限制軟件實體之間通訊的寬度和深度。

在迪米特法則中,對於一個對象,其朋友包括如下幾類:
(1) 當前對象自己(this);
(2) 以參數形式傳入到當前對象方法中的對象;
(3) 當前對象的成員對象;
(4) 若是當前對象的成員對象是一個集合,那麼集合中的元素也都是朋友;
(5) 當前對象所建立的對象。
任何一個對象,若是知足上面的條件之一,就是當前對象的「朋友」,不然就是「陌生人」。

迪米特法則可分爲狹義法則和廣義法則。在狹義的迪米特法則中,若是兩個類之間沒必要彼此直接通訊,那麼這兩個類就不該當發生直接的相互做用,若是其中的一個類須要調用另外一個類的某一個方法的話,能夠經過第三者轉發這個調用。

狹義的迪米特法則:能夠下降類之間的耦合,可是會在系統中增長大量的小方法並散落在系統的各個角落,它可使一個系統的局部設計簡化,由於每個局部都不會和遠距離的對象有直接的關聯,可是也會形成系統的不一樣模塊之間的通訊效率下降,使得系統的不一樣模塊之間不容易協調。
廣義的迪米特法則:指對對象之間的信息流量、流向以及信息的影響的控制,主要是對信息隱藏的控制。信息的隱藏可使各個子系統之間脫耦,從而容許它們獨立地被開發、優化、使用和修改,同時能夠促進軟件的複用,因爲每個模塊都不依賴於其餘模塊而存在,所以每個模塊均可以獨立地在其餘的地方使用。一個系統的規模越大,信息的隱藏就越重要,而信息隱藏的重要性也就越明顯。

迪米特法則的主要用途在於控制信息的過載
在類的劃分上,應當儘可能建立鬆耦合的類,類之間的耦合度越低,就越有利於複用,一個處在鬆耦合中的類一旦被修改,不會對關聯的類形成太大波及;
在類的結構設計上,每個類都應當儘可能下降其成員變量和成員函數的訪問權限;
在類的設計上,只要有可能,一個類型應當設計成不變類;
在對其餘類的引用上,一個對象對其餘對象的引用應當降到最低。

迪米特法則實例
實例說明
某系統界面類(如Form一、Form2等類)與數據訪問類(如DAO一、DAO2等類)之間的調用關係較爲複雜,如圖所示:

相關文章
相關標籤/搜索