1. 程式人生 > 遊戲 >突出重圍的獨立塔防精品《糖果災難》上線steam獲特別好評!

突出重圍的獨立塔防精品《糖果災難》上線steam獲特別好評!

面向物件設計原則

目錄
  • 目標:開閉原則
  • 指導:最小知識原則
  • 基礎:單一職責原則、可變性封裝原則
  • 實現:依賴倒轉原則、合成複用原則、里氏代換原則、介面隔離原則

單一職責原則SRP

Single Responsibility Principle

定義:一個物件應該只包含一個單一的職責,並且該職責被完整地封裝在一個類中

也就是說,就一個類而言,應該僅有一個引起他變化的原因

  • 資料職責:
    • 通過類屬性來體現
  • 行為職責:
    • 通過方法來體現

例子

重構後

開閉原則OCP

Open-Closed Principle

定義:一個軟體實體應該對擴充套件開發,對修改關閉

也就是說,在設計一個模組的時候,應當使這個模組可以在不被修改的前提下被擴充套件,即實現在不修改原始碼的情況下改變模組的行為

抽象化

  • 抽象化是開閉原則的關鍵
  • 通過“對可變性封裝”,找到系統的可變因素並將其封裝起來

例子

使用者可能要求使用不同的按鈕

里氏代換原則LSP

Liskov Substitution Principle

定義1:對每一個型別為S的o1,都有型別為T的o2,使得以T定義的所有程式P在所有的物件o2都代換成o1時,程式P的行為沒有發生變化,那麼型別S是型別T的子型別

定義2:所有引用父類的地方都必須能透明地使用其子類的物件

(第一種定義相對嚴格)

通俗地說,在軟體中如果能夠使用父類物件,那麼一定能夠使用子類物件

分析

  • LSP是實現OCP的重要方式之一
  • 在程式中儘量用基類型別對物件進行定義,在執行時再確定其子類型別,用子類物件來替換父類物件

例子

運用LSP,改為

依賴倒置原則DIP

Dependence Inversion Principle

定義:高層模組不應該依賴底層模組,它們都應該依賴抽象。抽象不應該依賴細節,細節應該依賴抽象

也就是說,要針對介面程式設計,不要針對實現程式設計

分析

  • 如果說開閉原則是OO設計的主要目的,那麼DIP就是OO設計的主要手段
  • 實現方式之一是:在程式碼中使用抽象類,而將具體類放在配置檔案中
  • 三種耦合方式
    • 零耦合
    • 具體耦合
    • 抽象耦合 √

例子

某系統提供資料轉換模組,可以將來自不同資料來源的資料轉換成多種格式,如資料庫、文字、XML等

由於需求的變化,可能需要增加新的資料來源或者檔案型別

介面隔離原則ISP

Interface Segregation Principle

定義:客戶端不應該依賴那些它不需要的介面

注意:這裡的介面指的是所定義的方法

也就是說,一旦一個介面太大,則需要把它分割成一些更細小的介面,使用該介面的客戶端僅需要知道與之相關的方法即可

怎樣定製介面

  • 一個介面只代表一個角色

  • 介面僅僅提供客戶端需要的行為

    • 隱藏客戶端不需要的行為,應為客戶端提供儘可能小的介面
  • 必須滿足單一職責原則

    • 在高內聚的前提下,方法越少越好。但也不必過於極端,否則會介面爆炸
  • 定製服務,為不同的客戶端提供寬窄不同的介面

    • 見下面的例子

例子

以下的系統定義了巨大的介面(胖介面)AbstractService

問題:

  • ClientA可能會呼叫operatorB
  • 假如ClientB修改operatorB,可能會影響到其他部分

修改:使用窄介面

合成複用原則CRP

Compose Reuse Principle

定義:儘量使用物件組合,而不是繼承來達到複用的目的

組合和聚合的區別

  • 聚合關係用一條帶空心菱形箭頭的直線表示,如下圖表示A聚合到B上,或者說B由A組成;
    • 與組合關係不同的是,整體和部分不是強依賴的,即使整體不存在了,部分仍然存在;例如, 部門撤銷了,人員不會消失,他們依然存在;
  • 組合關係用一條帶實心菱形箭頭直線表示,如下圖表示A組成B,或者B由A組成;
    • 但組合關係是一種強依賴的特殊聚合關係,如果整體不存在了,則部分也不存在了;例如, 公司不存在了,部門也將不存在了;

複用方法

  • 繼承複用(“白箱”複用)
    • 實現簡單,易於擴充套件
    • 破壞系統封裝性,暴露基類的實現細節
    • 繼承而來的實現是靜態的,可以看作是一種程式碼的複製,不能在執行時發生改變
  • 組合/聚合複用(“黑箱”複用)
    • 耦合度低,選擇性呼叫成員物件的操作
    • 可以在執行時動態進行

例子

不好的實現方法:繼承

問題:

  • 如果需要修改資料庫連線方式,需要修改DBUtil類原始碼
  • 如果StudentDAO和TeacherDAO要使用不同的連線方法,就需要新增一個新的DBUtil類,還要修改DAO使其繼承新的DBUtil類

修改:使用組合

最小知識原則(迪米特法則)LoD

Low of Demeter

定義:

(1)不要和“陌生人”說話

(2)只和你的直接朋友通訊

(3)每一個軟體單位對其他的單位都只有最少的知識,而且侷限於那些與本單位密切相關的軟體單位

簡單來說,迪米特法則就是指一個單位實體應該儘可能少的與其他實體發生相互作用

其實叫最小知識原則就好,因為只是一個指導方針,而不是"必須按照",說法則太嚴格了

什麼是直接朋友?

  1. 當前物件本身this
  2. 以引數形式傳入到當前物件方法中的物件
  3. 當前物件的成員變數
  4. 如果當前物件的成員物件是一個集合,那麼集合中的元素也都是朋友
  5. 當前物件所建立的物件

分析

狹義的迪米特法則

如果兩個類之間不必彼此直接通訊,那麼這兩個類就不應當發生直接的相互作用。

如果一個類需要呼叫另外一個類的方法,可以通過第三者轉發

以上圖為例,如果A想呼叫C的方法:

錯誤的做法:A.B.C.method()

正確的做法:

A.method(){
	B.wrapper();
}
B.wrapper(){
	C.method();
}
  • 好處:
    • 降低類的耦合
  • 壞處:
    • 會在系統中增加大量的小方法

廣義的迪米特法則

指物件之間的資訊流量、流向以及資訊的影響的控制,主要是對資訊隱藏的控制

  • 類的結構設計上,每一個類都應當儘量降低其成員變數和成員函式的訪問許可權
  • 類的設計上,一個型別應當設計成不變類
  • 對其它類的引用上,一個物件對其它物件的引用應當降到最低

例子

改進:

思考題

JDK中,java.util.Stackjava.util.Vector的子類,這樣的設計合理嗎?

  • 違反了合成複用原則,應該使用組合
  • 違反了里氏替換原則