1. 程式人生 > >面向物件設計原則

面向物件設計原則

自從上個世紀40~50年代計算機的興起,軟體開發行業逐漸的興起,達到60年代末,隨著面向過程的結構化高階程式語言的出現,可以說軟體開發進入到了一個非常鼎盛的時期。

但是在這個時期,隨著大量技術人員投入到這個行業,隨著軟體需求的不斷變化,以及需求的複雜的越來越高,就不可避免的出現了各種各樣的問題,這些問題甚至嚴重威脅到軟體開發這個行業。當時的開發者把這些問題稱是哪個年代的“軟體危機”。

“軟體危機”爆發的主要原因:

  1. 使用者需求的不明確;
  2. 軟體公司開發軟體的時候,並沒有一個相對正確的理論(套路);
  3. 軟體開發規模越來越大;
  4. 軟體的開發難度,或者說軟體的複雜度越來越大。

“軟體危機”具體的特徵體現:

  1. 軟體開發週期無法確定;
  2. 軟體開發成本越來越高,甚至無法控制;
  3. 產品質量不高,產品的功能無法滿足客戶需求,經常容易專案失敗;
  4. 當時的程式設計師技術,溝通等相關能力參差不齊,導致做出來的軟體質量不高;
  5. 軟體產品非常難於維護,具體原因:程式設計師的編碼能力,個人習慣;
  6. 當時軟體開發,不注重文件,軟體缺少適當的文件資料。

軟體開發流程

軟體開發,就跟建築行業一樣也需要有前期各種規劃,也需要有前期各種設計,同樣需要有農名工(程式猿|攻城獅)來參與,同樣需要對軟體進行各種測試|驗收,同樣也存在交付產品的過程。

如果我們不去向上述的方案進行操作的話,那麼就有可能會出問題。所以我們軟體開發也是非常嚴謹的事情,容不得半點馬虎!軟體開發,同樣也有各種需要管理的東西。

軟體開發中,比較經典的開發步驟:

瀑布模型:

  • 可行性分析
  • 需求分析
  • 軟體設計
  • 編碼
  • 測試
  • 交付產品
  • 維護

瀑布模型存在比較嚴重的問題:需求如果要發生變更,只能在極早期 越往後走,代價越大。所以我們又對軟體開發方案提出了一些改進:改進方案:迭代 + 瀑布模型

需求分析,軟體設計,程式設計 都有兩種方式:面向物件 面向過程,採用面向物件的思維習慣來,分析需求(OOA) 軟體設計(OOD) 程式設計(OOP),軟體開發過程中,怎麼樣去評價一個軟體設計的合不合理,好不好?

一個軟體開發方案好不好,它的 評價的標準是:

  1. 有沒有覆蓋使用者所提的所有的業務功能(覆蓋了所有的需求,也不一定就是一個設計非常好的軟體)
  2. 前期設計出來的各種文件或者圖紙,可讀性高不高,能不能被專案所有的干係人快速的理解。一個可讀性非常差文件,或者設計的圖紙,會給我們後期開發,測試,維護都會帶來巨大的影響
  3. 軟體設計的圖紙中,覆蓋的類,包,介面,以及其他的各種元件,複用性強不強,能不能被本專案,或者其他專案重複使用
  4. 軟體開發後期,如果業務需求需要發生變化,業務功能或者效能的擴充套件性高不高
  5. 後期軟體的維護,或者開發過程中程式碼的維護能力強不強(員工離職了,其他員工能不能看懂,能不能快速的理解我們的軟體結構)

注意: 上述的5個方向,歸納起來就是一句話,軟體設計過程中,是否能做到“高內聚,低耦合”?

**高內聚:**1個類只做1件事,以及1個方法只做1件事 具體來說:就是1個類只關注1個點,比如:使用者類只關注使用者資訊或者使用者的行為 它不會去關注其他類的屬性或者行為 。

如果一個類只關注1件事,並且內部每一個方法也只關注1個事,那麼說該類的內聚度非常的高,越高代表職責越清晰,程式碼的複用性越高,類與類之間的牽連越少。

耦合: 就是指類和類之間的關係緊密程度 如果一個類與例外一個類關係的非常緊密,那麼當其中的一個類需要發生變化的時候,呼叫類將也會跟著變化。

比如下面這段程式碼:

public class StudentServiceImpl implements IStudentService {

private IStudentDao studentDaoImpl = new StudentDaoImpl();

@Override
public void saveStudent(StudentBean stu) {
	// TODO Auto-generated method stub
	studentDaoImpl.saveStudent(stu);
	}
}

其中的StudentServiceImpl 和 StudentDaoImpl 兩個類的關係就非常的緊密。如果一旦StudentDaoImpl需要被替代,那麼StudentServiceImpl也就需要跟著修改程式碼。

耦合度這個東西,它是決定了應用軟體能否靈活支援需求變化的最大因素。緊密關係越緊,變化度越低,變化的代價越大。 但是我們又不能說就直接取消耦合,所以咱們只能降低“耦合度”,怎麼去降低耦合度?

就需要大家去遵循這個行業裡面,由前輩給我們總結出來一些設計原則,以及設計模式!

設計原則

第一個原則:單一原則

一句話描述:“1個類只幹1件事”,這是軟體設計中高內聚的最重要一點,換句話說:1個類只有1種職責,那麼這個職責的確定,需要根據類的物件來確定,看你正在關注物件的哪個方向。比如:學校系統中,學生物件來講,只需要關注學習,而不應該去關注他或者她怎麼當子女。只有1種情況能去改變我這個類。

也就是說在設計類時,類中每一個屬性,和每一個方法都必須要根這個類的職責,具有直接關係。如果某一個屬性或者某一個行為與本類的職責的無關,那麼這個類的設計,就一定違背了“單一原則”。

類的職責單一,也是一種降低耦合度的手段,物件的職責越少,耦合關聯度越低,程式碼越容易複用。當然單一職責,同樣適用於方法。當方法違背單一原則時,那麼方法的程式碼將會非常冗長,以及可能存在程式碼的重複,甚至程式碼的浪費!

第二原則:裡式替換原則

定義:子類可以出現在父類出現的任何地方,並且對於父類之前定義的規則,不出發生任何改變!

目的:為了維護現有的繼承體系,保證龍生龍,鳳生鳳,老鼠兒子依舊會打洞 這個原則是開閉原則的一種體現,或者是一種重要的保證,保證兒子在新增新的功能的時候,不會去改寫父類已經定義好的方法,從而去維護現有的繼承體系。

第三個原則:依賴倒置原則

軟體設計中,多層次之間,相互之間的依賴關係需要倒置為依賴抽象或介面,而不是直接依賴具體的實現。

具體表現為:

  1. 上層模組不應該直接依賴下層實現,而應該依賴於下層的抽象;
  2. 每一單獨的層次,抽象不應該依賴於細節,而細節應該依賴於抽象。

依賴倒置原則,用白話描述就是一句話:面向介面程式設計。

第四個原則:介面隔離原則(ISP)

在軟體設計中,我們所有的實現類在依賴介面的時候,都不應該去依賴那些用不到的方法,只需要依賴那些自己需要的方法,換句話說:就是在我們設計軟體的時候,不要強迫實現類去實現它不需要的方法。

介面隔離原則,具體體現有兩種情況:

  1. 介面的設計原則:介面的設計應該遵循最小介面原則,不要把使用者不使用的方法塞到同一個介面。(這裡的最小介面不一定就是一個方法一個介面,而是說一個介面只應該承擔一種責任,總之此處我們要掌握好一個適量即可);
  2. 介面隔離原則中的“最小化原則”,同樣適用於介面之間的繼承,那麼由繼承多個介面的介面,也同樣需要滿足“最小化原則”。一個介面只應該承擔一種責任,如果某一天,大家發現,某一個介面中,存在多個職責,那麼這個介面一定需要重新設計。

第五個原則:迪米特法則(LOD)

迪米特法則又稱為“最少知道原則”,它要求我們在定義類的時候,或者在實現類的方法的時候,儘量做到只與類的朋友進行互動,不與朋友之外的任何類有直接互動,因為一旦互動,就存在耦合,甚至有的類與類之間耦合度非常的高,換句話說:非朋友類如果需要修改,可以能導致你也跟著需要修改,這就違背了“開閉原則”。

第六原則:組合/聚合複用原則

組合聚合原則: 主要是一種推薦大家使用面向介面程式設計的一種原則,它的目的在於和繼承來區分“程式碼”複用問題,組合聚合是一種比較特殊的關聯關係,它同樣滿足“has”這種單詞所對應的場景,只不過它更多強調的部分和整體的關係,(比如:車和車的輪胎(聚合),部門和公司(組合))組合的耦合度比聚合的耦合度強。

繼承和組合聚合,都可以做到關於程式碼的複用,但是各有各的優缺點:

繼承的優點是:

  1. 父類定義的所有屬性和行為,子類都繼承了,相當於子類不需要再重複定義;
  2. 子類如果需要重寫或者擴充套件新的方法,非常的方便,並且對父類不會產生任何影響(定義上來講,但是替代上卻不是)

繼承的缺點是:

  1. 繼承複用破壞包裝。就繼承而言,父類對於子類是完全透明的,所有的子類都可以知道超類的方法。(違背了:迪米特法則);
  2. 父類如果發生改變,子類也會跟著發生改變;
  3. 父子關係,在java中採用extends實現,這種實現關係在編譯期間就可以確定下來,所以我們把這關係的建立,稱之為“靜態關係”,因為它不具備靈活性,我們從來沒聽說過“子類在執行時,可以動態的繫結父類”

組合聚合的優點:

  1. 組合聚合裡面,我們更多的採用面向介面程式設計(依賴倒轉原則)來實現類與類之間的鬆散耦合,那麼就說明:在類中訪問介面,就成了訪問物件的唯一方法;
  2. 介面是抽象的,它沒有細節,換句話說:在類中訪問介面中定義的方法,我們是不知道細節的,這種程式碼複用更近乎於“黑箱操作”。
  3. 面向介面程式設計永遠比面向細節程式設計耦合度低。比如:U盤細節容易發生變化,但是U盤與電腦之間對接的扣子,卻很少 發生變化;
  4. 面向介面程式設計中,介面的例項是在程式執行過程中,動態繫結的,換句話說:我可以按照自己的需求,來進行靈活的改變。

第七個原則:開閉原則

開閉原則: 開閉原則是所有原則的核心,其他任何原則最終的目的都是為開閉原則服務。開閉原則是指組成程式的程式碼(類、方法、模組····),他們應該對功能的擴充套件是開放的,而對功能的修改時關閉的。

具體表現為:

對功能的開放是開放的:對於一個系統而言,業務功能可能隨時在改變,也可能隨時在新增,該原則的意思是:系統支援我們去擴充套件新的功能,來滿足使用者的新需求。

對功能的修改時關閉的:我們在擴充套件新功能的時候,我們對於原功能的修改是關閉的。比如:我們系統以前只支援新手機,現在客戶要求我們還要支援老手機,那麼就要求我們:在支援老手機時,不要去修改支援新手機的任何程式碼。