設計模式是一個優秀的面向對象程式員的必修課,無論早學晚學,終究是一道邁不過去的坎。俗話說早死早超生(亂扯),盡早領會設計模式的魅力,我相信你會迷上程式設計的。
設計模式的本質是面向對象設計原則的實際運用,是對類的封裝性、繼承性和多态性以及類的關聯關系群組合關系的充分了解。這篇文章主要是為了準備設計模式的前置知識
一、工具
在這之前,推薦幾個比較趁手的工具
- PowerDesigner 是一款優秀的模組化軟體,常用來資料庫設計和建構面向對象的模型。我們需要用它來建構UML圖,收費
- draw 一個線上diagram的制作網站,可以用其制作簡單的UML圖。容易上手,超級好用。當然也有visio等等,不過前者比較簡單友善
- Intellij IDEA 部落格中的代碼是基于Java的,其中IDEA中可以自動生成出各類之間的類圖(貌似Jetbrain家族的産品都可以)。同時,也需要在IDEA中下載下傳SequenceDiagram這個插件,它可以生成時序圖
二、設計模式分類
- 到如今,從狹義上說,設計模式還是從GOF四人組所收錄的23中設計模式
範圍\目的 | 建立型模式 | 結構型模式 | 行為型模式 |
---|---|---|---|
類模式 | 工廠方法 | (類)擴充卡 | 模闆方法 解釋器 |
對象模式 | 單例 原型 抽象工廠 建造者 | 代理 (對象)擴充卡 橋接 裝飾 外觀 享元 組合 | 政策 指令 職責鍊 狀态 觀察者 中介者 疊代器 通路者 備忘錄 |
按目的
- 建立型模式:用來建立對象,将對象的建立與使用分離
- 結構性模式:将類或者對象按照某種布局組成更大的結構
- 行為性模式:描述類之間的互相協調以完成單個對象無法完成的任務
按作用範圍
- 類模式:用于處理類之間的關系,通過繼承實作
- 對象模式:用于處理對象之間的關系,通過組合、聚合或者依賴實作
三、再談面向對象
我們從面向對象的三大特性來聊一聊面向對象語言
繼承
- A繼承了B,我們稱A為子類/派生類/特化類,B為父類/基類/超類/泛化類
- A擁有B的全部特性,同時,A還可以再增加自己獨有的特性
- 繼承是從子類的角度出發,由低向上
多态
- 多态是從父類的角度出發,由頂至下
- 多态可以通過一種方式的引用,而獲得不同方式的行為
-
Parent p = new Son(); p.getSon();
Parent p = new Daughter(); p.getDaughter();
- 後續在工廠方法模式中會進一步領略多态的妙用
封裝
- 封裝不僅僅是隐藏資料,封裝是隐藏了所有東西
- 因為有了封裝,我們可以更好地增加程式的透明度
- public等權限也是封裝的一部分,對不可信的事物進行資訊隐藏
四、UML
Unified Modeling Language 是面向對象模組化語言的國際标準,共分為用例圖,類圖,對象圖,狀态圖,活動圖,時序圖,協作圖,構件圖,部署圖等9種圖。本文主要說的是和設計模式關聯比較緊密的類圖
1.類之間的關系
前文作用範圍中有提到這些關系
依賴
- 對象之間耦合度最弱的一種關聯方式
- 通常依賴即是調用被依賴者的方法
- use - a 上圖可了解為 人使用手機,人依賴手機
寫在設計模式之前
繼承
- 對象之間耦合度最強的一種關系
- is - a 上圖可了解為 學生和教師泛化/繼承了人
寫在設計模式之前
聚集
- 是一種強關聯方式
- 是整體和部分的關系
- has - a
寫在設計模式之前 上圖可了解為 大學聚集了教師
同時,飛機場和飛機也是一種聚集關系
組合
- 比聚集還要強關聯,整體對象可以控制部分對象的生命周期
- cxmtains - a
寫在設計模式之前 上圖可了解為 嘴是頭的一部分,即嘴組合成了頭
通時,汽車和輪胎也是一種聚集關系
2.類圖
類圖是一種顯示類、接口、寫作已經它們之間的靜态結構和關系的一種靜态模型
- 類圖中的類可以用面向對象語言來直接封裝
- 類圖中的類包含屬性(字段),方法以及他們的可見性
- 其中
+ - # ~ public private protected friendly - 在平常的使用中,我們不會單獨的使用某一個類,而是把類以及之前說的類之間的關系一起運用,這也是設計模式必須掌握的知識
在這幅圖中,長方形類和圓形類繼承了Graph,然後通路類Client使用了Graph
3.時序圖
- 時序圖表示了整個程式的流程,我們在設計模式或者說在面向對象的學習中最常用的就是類圖和時序圖
- 我們可以通過IDEA的SequenceDiagram插件來獲得我們程式的時序圖
五、七種設計原則
1.單一職責原則
Single Responsibility Principle, SRP
- 一個對象應該隻包含單一的職責,并該職責被完整地封裝在一個類中
- 是實作高内聚,低耦合的指導方針
- 一個類應當盡可能承擔一種資料職責/通過其屬性(字段)展現,或行為職責/通過其方法展現
執行個體
⟶ 按 照 單 一 職 責 原 則 重 構 ⟶ \longrightarrow 按照單一職責原則重構 \longrightarrow ⟶按照單一職責原則重構⟶
2.開閉原則
Open Closed Principle, OCP
- 項目中的子產品,類與接口,方法應當對擴充開放,對修改關閉。即在不修改軟體實體的基礎上去擴充其功能
- 是面向對象程式設計的終極目标
- 可以通過抽象限制,封裝變化來實作開閉原則,即面向抽象程式設計
- 其中工廠方法模式就符合開閉原則
執行個體
⟶ 重 構 之 後 ⟶ \longrightarrow 重構之後 \longrightarrow ⟶重構之後⟶
3.裡式替換原則
Liskov Substitution Principle, LSP
- 所有引用基類的地方必須能透明地使用其子類對象,這保證了繼承複用是可靠的
- 是開閉原則的重要實作方式之一
- 通俗上說,子類繼承父類時,除添加新的方法完成新增功能外,盡量不要重寫父類的方法 如上圖,幾維鳥重寫了鳥的方法,此時幾維鳥不是鳥
寫在設計模式之前
4.依賴倒置原則
Dependence Inversion Principle, DIP
- 是實作開閉原則的重要途徑之一
- 高層子產品不應該依賴低層子產品,兩者都應該依賴其抽象;抽象不應該依賴細節,細節應該依賴抽象,即面向接口和抽象程式設計,不要面向實作程式設計
- 因為抽象是不變的,依賴于抽象是面向對象設計的精髓,也是依賴倒置原則的核心
- 實作方式:在代碼中使用抽象類,而将具體類放在配置檔案中
我有個問題,Spring中的依賴注入是不是也是依賴倒置原則的具體表現?
5.接口隔離原則
Interface Segregation Principle, ISP
- 用戶端不應該依賴那些它不需要的接口
- 一個接口太大,則應該将它分割為更小的接口。即要使用專門的接口而不是單一的接口
和單一職責原則的差別
- 單一職責原則注重的是職責,而接口隔離原則注重的是對接口依賴的隔離
- 單一職責原則主要是限制類,它針對的是程式中的實作和細節;接口隔離原則主要限制接口,主要針對抽象和程式整體架構的建構
寫在設計模式之前
6.迪米特法則
Law of Demeter, LoD
- 軟體實體應當盡可能少與其他實體進行互動
- 目前對象本身(this);
- 以參數形式傳入到目前對象方法中的對象;
- 目前對象的成員對象;
- 如果目前對象的成員對象是一個集合,那麼集合中的元素也都是 朋友;
- 目前對象所建立的對象
- 執行個體 ⟶ 修 改 後 ⟶ \longrightarrow 修改後 \longrightarrow ⟶修改後⟶
寫在設計模式之前 寫在設計模式之前
7.合成複用原則
Composite Reuse Principle,CRP
- 合成複用原則和裡式替換原則相輔相成,兩者都是開閉原則的具體實作規範
- 盡量使用對象組合而不是繼承達到複用目的。即要盡量使用組合/聚合關系,少用繼承,在使用繼承之後要考慮裡式替換原則 ⟶ 重 構 之 後 ⟶ \longrightarrow 重構之後 \longrightarrow ⟶重構之後⟶
寫在設計模式之前 寫在設計模式之前
8.關系
- 開閉原則是總綱,對擴充開放,修改關閉
- 裡式替換和合成複用說明組合之間的關系
- 依賴倒置說明要面向接口程式設計
- 接口隔離和單一職責說明類和接口的設計
- 迪米特告訴我們要降低耦合度