【设计模式】 面向对象六大设计原则
面向?qū)ο笤O(shè)計(jì)的六大原則 : 單一職責(zé)原則, 里氏替換原則, 依賴倒置原則, 接口隔離原則, 迪米特法則, 開(kāi)閉原則;
一. 單一職責(zé)原則
1. 單一職責(zé)簡(jiǎn)介
單一職責(zé)定義 : 有且只有一個(gè)原因引起類的變化, 一個(gè)接口 或者 類 只有一個(gè)職責(zé);
單一職責(zé)的好處 :?
-- 復(fù)雜性 : 降低類的復(fù)雜性, 對(duì)類或接口的職責(zé)有清晰明確定義;
-- 可讀性 : 提高可讀性;
-- 維護(hù) : 提高可維護(hù)性;
-- 變更風(fēng)險(xiǎn) : 降低變更引起的風(fēng)險(xiǎn), 接口改變只影響相應(yīng)的實(shí)現(xiàn)類, 不影響其他類;
2. 單一職責(zé)示例
(1) 反面示例
示例要求 : 創(chuàng)建一個(gè)繪圖系統(tǒng)
-- 繪圖 : 可以繪制圓形, 矩形;
-- 顯示 : 顯示繪制好的圖形;
UML 圖 : 明顯下圖不符合單一職責(zé)原則, 繪制圓形, 矩形, 顯示圓形, 矩形 都集成在了一個(gè)接口中, 明顯不符合單一職責(zé)原則;
(2) 正面示例
修改一下上面的示例, 我們可以得到下面的 UML 圖 :?
-- 說(shuō)明 : 將 繪制 和 顯示 分別封裝在兩個(gè)接口中, 讓一個(gè)實(shí)現(xiàn)類同時(shí)繼承這兩個(gè)接口, 此時(shí)實(shí)現(xiàn)了接口的單一職責(zé);
3. 單一職責(zé)實(shí)踐
單一職責(zé)適用 : 單一職責(zé)同時(shí)適用于接口, 類, 方法;
-- 接口 : 接口一定要做到單一職責(zé);
-- 類 : 類的單一職責(zé)比較難以實(shí)現(xiàn),?盡量做到只有一個(gè)原因引起變化;
-- 方法 : 一個(gè)方法盡可能做一件事, 能分解就分解, 分解到原子級(jí)別;
二. 里氏替換原則
1. 里氏替換簡(jiǎn)介
(1) 里氏替換定義
里氏替換 : 所有 引用基類的地方 必須能 透明地使用其子類的對(duì)象;
-- 子類替換父類?: 只要 父類出現(xiàn)的地方子類就可以出現(xiàn), 替換為子類也不會(huì)產(chǎn)生任何錯(cuò)誤, 使用者不需要知道父類還是子類;
(2) 繼承的優(yōu)缺點(diǎn)
繼承優(yōu)點(diǎn) :?
-- 代碼共享 : 共享代碼, 子類都擁有父類的方法和屬性, 將 父類的代碼共享給了子類;
-- 重用性 : 提高代碼的重用性, 子類重用父類的代碼;
-- 子父類異同 : 子類形似父類, 異于父類, 父子都不同;
-- 擴(kuò)展性 : 提高代碼的可擴(kuò)展性, 子類就可以為所欲為了, 子類可以隨意擴(kuò)展父類;
-- 開(kāi)放性 : 提高產(chǎn)品或項(xiàng)目的開(kāi)放性, 父類隨意擴(kuò)展, 開(kāi)放性隨之增加了;
繼承缺點(diǎn) :?
-- 侵入性 : 繼承是侵入性的, 子類 強(qiáng)制繼承 父類的方法和屬性;
-- 靈活性 : 降低代碼的靈活性, 子類必須擁有父類的屬性和方法, 子類收到了父類的約束, 這是從子類的角度講得;
-- 耦合性 : 增強(qiáng)了耦合性, 父類的屬性和方法被修改時(shí), 還需要顧及其子類, 可能會(huì)帶來(lái)大量的重構(gòu), 這是從父類的角度講的;
2. 里氏替換規(guī)范含義
(1) 子類完全實(shí)現(xiàn)父類方法
替換方法 : 定義一個(gè)接口或抽象類, 編碼實(shí)現(xiàn)一個(gè)子類繼承或?qū)崿F(xiàn)該接口或抽象類, 調(diào)用類直接傳入接口或抽象類;
示例說(shuō)明 :?
-- UML 圖 :?
-- Client 場(chǎng)景類 : 在該場(chǎng)景中創(chuàng)建一個(gè) Player 玩家, 然后為 Player 設(shè)置槍, 之后開(kāi)槍 kill;
-- Player 類 : 玩家類, 該類有一個(gè) IGun 抽象類成員變量;
-- IGun : 所有槍的基類, 所有的槍都要繼承該類;
-- 說(shuō)明 : 在 Player 中 IGun 類型的成員變量都可以使用 IGun 的三個(gè)實(shí)現(xiàn)類來(lái)替代;
(2) 子類個(gè)性
里氏替換單向性 : 子類可以有自己的方法和屬性, 里氏替換可以正著用, 使用子類替換子類, 但是反過(guò)來(lái)不可以, 子類出現(xiàn)的地方, 父類不能使用;
(3) 覆蓋方法參數(shù)放大
前置后置條件 : 子類方法中得前置條件必須與超類中被覆寫的前置條件相同或者更寬松;
-- 前置條件 : 關(guān)于參數(shù), 輸入的參數(shù)必須符合要求, 才會(huì)執(zhí)行, 必須滿足的條件;
-- 后置條件 : 關(guān)于返回值, 方法執(zhí)行完之后, 需要返回一個(gè)結(jié)果, 這個(gè)結(jié)果的標(biāo)準(zhǔn);
重寫與多態(tài) : 如果子類的前置條件與父類相同, 那么是重寫方法, 如果子類更寬松, 那么就是多態(tài), 生成了新的方法;
(4) 覆蓋方法返回值縮小
返回值 : 父類方法返回值類型 F, 子類方法返回值類型 S, 里氏替換原則是 S 范圍必須小于 F;?
-- 重寫 : 父類子類參數(shù)相同, S 范圍小于 F;
-- 重載 : 父類 子類 方法參數(shù)類型或者數(shù)量不同, 如果要符合里氏替換要求的話, 子類參數(shù)必須 >= 父類參數(shù), 即不能讓子類自己定義的方法被調(diào)用;
3. 里氏替換注意點(diǎn)
避免子類個(gè)性 : 如果想要使用里氏替換, 盡量避免讓子類擁有自己?jiǎn)为?dú)的成員變量 或者 方法, 如果子類個(gè)性多了, 子類父類關(guān)系很難調(diào)和;
-- 里氏替換缺點(diǎn) : 將子類當(dāng)做父類用, 抹殺了子類的個(gè)性;
-- 里氏替換優(yōu)點(diǎn) : 將子類單獨(dú)作為一個(gè)業(yè)務(wù)來(lái)使用, 會(huì)讓代碼間的耦合關(guān)系都復(fù)雜, 缺乏類替換標(biāo)準(zhǔn);
三. 依賴倒置原則
1. 依賴倒置簡(jiǎn)介
(1) 依賴倒置定義
模塊與抽象 :?
-- 低層模塊 : 不可分割的原子邏輯是低層模塊;
-- 高層模塊 : 原子邏輯組合成高層模塊;
-- 抽象 : 接口或者抽象類, 不能被實(shí)例化;
-- 細(xì)節(jié) : 實(shí)現(xiàn)類, 實(shí)現(xiàn)接口或繼承抽象類就是細(xì)節(jié), 可以被實(shí)例化;
依賴倒置定義 :?
-- 模塊依賴 : 高層模塊不應(yīng)該依賴底層模塊, 兩者都依賴其抽象, 實(shí)現(xiàn)類之間不發(fā)生依賴關(guān)系, 依賴關(guān)系通過(guò)接口或抽象類產(chǎn)生;
-- 抽象不依賴細(xì)節(jié) : 抽象不依賴細(xì)節(jié), 接口或抽象類不依賴與實(shí)現(xiàn)類;
-- 細(xì)節(jié)依賴抽象 : 實(shí)現(xiàn)類依賴接口或抽象類;
(2) 依賴倒置好處
依賴倒置好處 : 依賴倒置原則可以 減少類之間的耦合, 提高系統(tǒng)穩(wěn)定性, 降低并發(fā)風(fēng)險(xiǎn), 提高代碼可讀性 和 可維護(hù)性;
-- 耦合 :?
-- 并發(fā)風(fēng)險(xiǎn) :?
-- 可讀性 :?
-- 可維護(hù)性 :?
2. 依賴倒置注入實(shí)現(xiàn)
(1) 構(gòu)造函數(shù)依賴對(duì)象
注入方法 : 通過(guò) 構(gòu)造函數(shù)參數(shù) 聲明依賴對(duì)象, 即構(gòu)造函數(shù)注入;
(2) Setter 方法依賴對(duì)象
注入方法?: 通過(guò) Setter 函數(shù) 參數(shù) 聲明依賴對(duì)象, 即構(gòu)造函數(shù)注入;
(3) 接口注入依賴對(duì)象
注入方法?: 在接口方法的參數(shù)中聲明依賴對(duì)象, 即接口注入;
3. 依賴倒置遵循規(guī)則
依賴倒置本質(zhì) : 通過(guò) 抽象 即 接口或者抽象類, 使 各個(gè)類 和 模塊實(shí)現(xiàn)彼此獨(dú)立, 實(shí)現(xiàn)模塊間 松耦合;
(1) 類有抽象
抽象所有的類 : 每個(gè)類盡量都有接口或者抽象類, 最好接口和抽象類都有;
(2) 變量類型抽象
變量類型抽象 : 變量的表面類型盡量都定義成抽象類;
-- 注意 : 不是絕對(duì)的, 一些工具類, 組件不必定義抽象;
(3) 類派生控制
派生控制 : 任何類不能從具體類派生;
-- 開(kāi)發(fā)階段 : 在開(kāi)發(fā)階段, 從具體類派生類是不允許的, 這樣會(huì)增加繼承的層次, 加大后期維護(hù)難度, 盡量將繼承層次控制在 2 層以內(nèi);
-- 維護(hù)階段 : 維護(hù)階段可以出現(xiàn)從具體類派生的情況, 這樣更有利于系統(tǒng)的穩(wěn)定性;
(4) 不要覆蓋方法
不要覆蓋方法 : 盡量不要覆蓋方法, 如果方法在抽象類中已經(jīng)實(shí)現(xiàn), 子類不要覆蓋;
-- 覆蓋缺點(diǎn) : 會(huì)對(duì)系統(tǒng)穩(wěn)定性產(chǎn)生影響;
(5) 結(jié)合里氏替換
里氏替換 : 父類出現(xiàn)的地方子類就能出現(xiàn),?
接口,抽象類,實(shí)現(xiàn)類規(guī)則 :?
-- 接口 : 負(fù)責(zé)定義 public 屬性和方法, 并聲明與其它對(duì)象的依賴關(guān)系;
-- 抽象類 : 負(fù)責(zé)公共構(gòu)造部分實(shí)現(xiàn);
-- 實(shí)現(xiàn)類 : 準(zhǔn)確地實(shí)現(xiàn)業(yè)務(wù)邏輯, 適當(dāng)時(shí)候?qū)Ω割愡M(jìn)行細(xì)化;
4. 依賴倒置注意點(diǎn)
(1) 依賴正置
依賴正置 :?類之間的依賴是實(shí)體類之間的依賴, 即面向現(xiàn)實(shí)編程;
-- 例如 : 我開(kāi)寶馬車, 我 是 人類型, 寶馬車 是車 類型, 依賴倒置就是 人 依賴 車, 依賴正置就是 我 依賴 寶馬車;
(2) 依賴倒置使用場(chǎng)合
依賴倒置使用場(chǎng)景 :?
-- 小項(xiàng)目 : 依賴倒置在小項(xiàng)目中得有點(diǎn)很難體現(xiàn)出來(lái), 是否采用依賴倒置原則影響不大;
-- 大項(xiàng)目 : 項(xiàng)目越大, 需求改變?cè)蕉? 采用依賴倒置原則設(shè)計(jì)的接口或抽象類 對(duì) 實(shí)現(xiàn)類的約束, 會(huì)大大減少維護(hù)成本;
(3) 依賴倒置與其它原則
依賴倒置與開(kāi)閉原則聯(lián)系 : 依賴倒置原則是實(shí)現(xiàn)開(kāi)閉原則的重要途徑, 依賴倒置原則沒(méi)有實(shí)現(xiàn), 無(wú)法對(duì)擴(kuò)展開(kāi)放, 對(duì)修改關(guān)閉;
四. 接口隔離原則
1. 接口隔離原則簡(jiǎn)介
(1) 接口分類
接口分類 :?
-- 實(shí)例接口 : Java 中得一個(gè)類, 對(duì)一個(gè)類型的描述, 例如 Student xiaoming; 其中的 Student 類就是實(shí)例接口, 這不是我們這里所關(guān)心的;
-- 類接口 : Java 中得 Interface 接口, 這是我們所說(shuō)的接口隔離原則中得接口;
(2) 接口隔離定義
接口隔離定義 : 建立單一的接口, 功能盡量細(xì)化, 不要建立臃腫的接口;
-- 不需要的接口 : 客戶端盡量不依賴其不需要的接口, 客戶端需要什么接口就提供什么接口, 剔除不需要的接口, 對(duì)接口進(jìn)行細(xì)化, 保持接口方法最少;
-- 最小接口 : 類間的依賴關(guān)系應(yīng)該建立在最小接口上, 細(xì)化接口;
(3) 接口隔離 與 單一職責(zé)區(qū)別
單一職責(zé) 與 接口隔離 區(qū)別 :?
-- 單一職責(zé) : 注重職責(zé), 注重業(yè)務(wù)邏輯上得劃分;
-- 接口隔離 : 注重的是接口的方法盡量少;
(4) 模塊 與 接口
模塊與接口 : 每個(gè)模塊盡量都提供一個(gè)單一接口, 有幾個(gè)模塊, 就由幾個(gè)接口, 避免建立一個(gè)龐大臃腫的接口對(duì)應(yīng)所有的模塊;
2. 接口隔離原則實(shí)現(xiàn)
(1) 接口盡量小
拆分接口 : 接口隔離的核心定義, 不出現(xiàn)臃腫的接口;
-- 限制 : 接口小有限度, 不能違反單一職責(zé)原則, 不要將一個(gè)業(yè)務(wù)邏輯拆分成兩個(gè)接口;
-- 要求 : 根據(jù)接口隔離原則拆分接口時(shí), 必須滿足單一職責(zé)原則;
(2) 接口高內(nèi)聚
高內(nèi)聚 : 提高接口, 類, 模塊的處理能力, 減少對(duì)外界交互;
-- 具體方法 : 接口中盡量少公布public 方法, 對(duì)外公布的 public 方法越少, 變更的風(fēng)險(xiǎn)就越小, 有利于后期的維護(hù);
(3) 定制服務(wù)
定制服務(wù) :?
-- 起源 : 系統(tǒng)模塊間的耦合需要有相互訪問(wèn)的接口, 這里就需要為各個(gè) 客戶端 的訪問(wèn)提供定制的服務(wù)接口;
-- 要求 : 只提供訪問(wèn)者需要的方法, 不需要的就不提供;
(4) 接口隔離限度
接口隔離局限性 :?
-- 粒度小 : 接口粒度越小, 系統(tǒng)越靈活, 但是同時(shí)使系統(tǒng)結(jié)構(gòu)復(fù)雜, 開(kāi)發(fā)難度增加, 降低了系統(tǒng)的可維護(hù)性;
-- 粒度大 : 靈活性降低, 無(wú)法提供定制服務(wù), 增大項(xiàng)目風(fēng)險(xiǎn);
3. 原子接口劃分原則
原子接口或類 : 接口隔離原則 即適用于 接口的定義, 也適用于對(duì)類的定義, 那么這樣的接口和類就是 原子接口 和 原子類;
(1) 接口模塊一一對(duì)應(yīng)
接口模塊對(duì)應(yīng)關(guān)系 : 一個(gè)接口只服務(wù)于一個(gè)子模塊 或 業(yè)務(wù)邏輯;
(2) 壓縮方法
方法壓縮 : 通過(guò)業(yè)務(wù)邏輯, 壓縮接口中得 public 方法, 減少接口的方法的數(shù)量;
(3) 接口修改
修改適配 : 盡量去修改已經(jīng)污染的接口, 如果變更風(fēng)險(xiǎn)較大, 采用適配器模式進(jìn)行轉(zhuǎn)化處理;
五. 迪米特法則
1. 迪米特法則定義
迪米特法則 : 最少知識(shí)原則, 一個(gè)對(duì)象應(yīng)該對(duì)其它對(duì)象有最少的了解, 即一個(gè)類對(duì)自己需要耦合或者調(diào)用的類知道的最少;
2. 低耦合要求
(1) 只和朋友交流
只與朋友通信 :?
-- 朋友形成 : 一個(gè)對(duì)象與其它對(duì)象有耦合關(guān)系, 兩個(gè)對(duì)象間的耦合使兩個(gè)對(duì)象成為朋友關(guān)系;
-- 朋友定義 : 出現(xiàn)在 類 成員變量 , 方法參數(shù)返回值 中的類是朋友, 其它位置的不是朋友, 在方法體內(nèi)出現(xiàn)的其它類不是朋友;
(2) 朋友距離
朋友間必須保持距離 :?
-- 距離太近示例 : 朋友間不能無(wú)話不說(shuō), 無(wú)所不知, 類 A 與 B 耦合, B 將很多方法暴露給 A, 兩個(gè)類之間的的耦合關(guān)系非常牢固, 這明顯違反設(shè)計(jì)原則;
-- 保持距離方法 : 將 類 B 暴露給 A 的方法封裝, 暴露的方法越少越好, 類 B 高內(nèi)聚, 與 A 低耦合;
-- 設(shè)計(jì)方法 : 一個(gè)類的 public 方法越多, 修改時(shí)涉及的范圍也就越大, 變更引起的風(fēng)險(xiǎn)也就越大, 在系統(tǒng)設(shè)計(jì)時(shí)要注意, 能用 private 就用private , 能用 protected 就用 protected, 能少用 public 就少用 public, 能加上 final 就加上 final;
(3) 類方法位置確定
原則 : 如果一個(gè)方法放在本類, 不增加類間關(guān)系, 不對(duì)類產(chǎn)生負(fù)面影響, 就放在本類中;
3. 迪米特法則注意事項(xiàng)
迪米特法則核心原則 : 類間解耦, 弱耦合, 耦合降低, 復(fù)用率提高;
-- 局限性 : 類間的耦合性太低, 會(huì)產(chǎn)生大量的中轉(zhuǎn)或跳轉(zhuǎn)類, 會(huì)導(dǎo)致系統(tǒng)的復(fù)雜性提高, 加大維護(hù)難度;
六. 開(kāi)閉原則
1. 開(kāi)閉原則定義
開(kāi)閉原則定義 : 軟件的實(shí)體 類, 模塊, 函數(shù) 應(yīng)該對(duì)擴(kuò)展開(kāi)放, 對(duì)修改關(guān)閉; 即 軟件實(shí)體 應(yīng)該 通過(guò)擴(kuò)展實(shí)現(xiàn)變化, 不是通過(guò) 修改已有的代碼實(shí)現(xiàn)變化;
--?軟件實(shí)體 : 軟件產(chǎn)品中得 邏輯 劃分模塊, 抽象 和 類, 方法;
2. 變化
各種變化 :?
-- 邏輯變化 : 變化一個(gè)邏輯模塊, 其它模塊不改變, 所有的依賴 或 關(guān)聯(lián)關(guān)系都按照相同的邏輯處理;
-- 子模塊變化 : 一個(gè)邏輯模塊變化, 會(huì)影響其它模塊, 低層次模塊 (原子邏輯模塊) 變化會(huì)引起高層次模塊 (原子模塊組合) 的變化, 通過(guò)擴(kuò)展完成變化時(shí), 高層次模塊需要修改;
3. 開(kāi)閉原則好處
開(kāi)閉原則好處 :?
-- 利于測(cè)試 : 如果改變軟件內(nèi)容, 需要將所有的測(cè)試流程都執(zhí)行一遍, 如 單元測(cè)試, 功能測(cè)試, 集成測(cè)試等, 如果只是擴(kuò)展, 只單獨(dú)測(cè)試擴(kuò)展部分即可;
-- 提高復(fù)用性 : 所有邏輯都從原子邏輯組合, 原子邏輯粒度越小, 復(fù)用性越大; 這樣避免相同邏輯存在, 修改時(shí)需要修改多個(gè)此相同邏輯;
-- 提高可維護(hù)性 : 維護(hù)一個(gè)類最好的方式是 擴(kuò)展一個(gè)類, 而不是修改一個(gè)類, 如果需要修改需要讀懂源碼才能修改, 擴(kuò)展的話只需要了解即可, 直接繼承擴(kuò)展;
總結(jié)
以上是生活随笔為你收集整理的【设计模式】 面向对象六大设计原则的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【网站部署】解析二级域名并部署网站
- 下一篇: 【Android应用开发】分享一个录制