網(wǎng)站頭部導航代碼網(wǎng)站seo優(yōu)化怎么做
03.開閉原則詳細介紹
目錄介紹
- 01.問題思考的分析
- 02.如何理解開閉原則
- 03.開閉原則的背景
- 04.開閉原則比較難學
- 05.實現(xiàn)開閉原則方式
- 06.畫圖形案例分析
- 07.銀行業(yè)務案例分析
- 08.開閉原則優(yōu)缺點
- 09.開閉原則的總結
推薦一個好玩網(wǎng)站
一個最純粹的技術分享網(wǎng)站,打造精品技術編程專欄!編程進階網(wǎng)
https://yccoding.com/
設計模式Git項目地址:https://github.com/yangchong211/YCDesignBlog
單一職責原則(SRP)是面向對象設計的重要原則,強調(diào)一個類或模塊應僅負責完成一個特定的職責或功能。通過將復雜的功能分解為多個粒度小、功能單一的類,可以提高系統(tǒng)的靈活性、可維護性和可擴展性。
本文詳細介紹了如何理解單一職責原則,包括方法、接口和類層面的應用,并通過具體例子解釋了其優(yōu)勢和判斷標準。此外,還探討了在實際開發(fā)中如何平衡類的設計,避免過度拆分導致的復雜性增加。
01.問題思考的分析
- 什么叫作開閉原則,他的主要用途是什么?
- 如何做到拓展開放,修改封閉這一準則,結合案例說一下如何實現(xiàn)?
- 你平常是如何理解開閉原則的,判斷的標準是什么?
02.如何理解開閉原則
開閉原則的英文全稱是 Open Closed Principle,簡寫為 OCP。它的英文描述是:software entities (modules, classes, functions, etc.) should be open for extension , but closed for modification。
我們把它翻譯成中文就是:軟件實體(模塊、類、方法等)應該“對擴展開放、對修改關閉”。
這個描述比較簡略,如果我們詳細表述一下,那就是,添加一個新的功能應該是,在已有代碼基礎上擴展代碼(新增模塊、類、方法等),而非修改已有代碼(修改模塊、類、方法等)。
03.開閉原則的背景
在軟件的生命周期內(nèi),因為變化、升級和維護等原因需要對軟件原有代碼進行修改時,可能會將錯誤引入原本已經(jīng)經(jīng)過測試的舊代碼中,破壞原有系統(tǒng)。
因此,當軟件需要變化時,我們應該盡量通過擴展的方式來實現(xiàn)變化,而不是通過修改已有的代碼來實現(xiàn)。
當然,在現(xiàn)實開發(fā)中,只通過繼承的方式來升級、維護原有系統(tǒng)只是一個理想化的愿景,因此,在實際的開發(fā)過程中,修改原有代碼、擴展代碼往往是同時存在的。
軟件開發(fā)過程中,最不會變化的就是變化本身。產(chǎn)品需要不斷地升級、維護,沒有一個產(chǎn)品從第一版本開發(fā)完就再沒有變化了,除非在下個版本誕生之前它已經(jīng)被終止。
產(chǎn)品需要升級,修改原來的代碼就可能會引發(fā)其他的問題。那么如何確保原有軟件模塊的正確性,以及盡量少地影響原有模塊,答案就是盡量遵守本章要講述的開閉原則。
04.開閉原則比較難學
個人覺得,開閉原則是 SOLID 中最難理解、最難掌握,同時也是最有用的一條原則。
之所以說這條原則難理解,那是因為,“怎樣的代碼改動才被定義為‘擴展’?怎樣的代碼改動才被定義為‘修改’?怎么才算滿足或違反‘開閉原則’?修改代碼就一定意味著違反‘開閉原則’嗎?”等等這些問題,都比較難理解。
之所以說這條原則難掌握,那是因為,“如何做到‘對擴展開放、修改關閉’?如何在項目中靈活地應用‘開閉原則’,以避免在追求擴展性的同時影響到代碼的可讀性?”等等這些問題,都比較難掌握。
之所以說這條原則最有用,那是因為,擴展性是代碼質(zhì)量最重要的衡量標準之一。在 23 種經(jīng)典設計模式中,大部分設計模式都是為了解決代碼的擴展性問題而存在的,主要遵從的設計原則就是開閉原則。
05.實現(xiàn)開閉原則方式
為了實現(xiàn)開閉原則,常用的設計技術有以下幾種:
- 抽象類和接口:通過定義抽象類和接口來約定行為,然后通過繼承和實現(xiàn)這些抽象類和接口來擴展功能。
- 策略模式:將算法的實現(xiàn)分離到不同的類中,通過組合方式來實現(xiàn)不同的行為。
- 裝飾器模式:通過對對象進行包裝,動態(tài)地添加新的行為或功能。
06.畫圖形案例分析
6.1 普通案例實現(xiàn)
假設有一個圖形繪制程序,程序需要能夠繪制不同形狀的圖形,比如矩形、圓形和三角形。最初的設計可能會像這樣:
public class GraphicEditor {public void draw(Shape shape) {if (shape.m_type == 1) {drawRectangle();} else if(shape.m_type == 2) {drawCircle();}}public void drawRectangle() {System.out.println("畫長方形");}public void drawCircle() {System.out.println("畫圓形");}class Shape {int m_type;}class Rectangle extends Shape {Rectangle() {super.m_type=1;}}class Circle extends Shape {Circle() {super.m_type=2;}}
}
我們來看看,這個代碼,初看是符合要求了,再想想,要是我增加一種形狀呢? 比如增加三角形.
- 首先,要增加一個三角形的類, 繼承自Shape
- 第二,要增加一個畫三角形的方法drawTriage()
- 第三,在draw方法中增加一種類型type=3的處理方案
在這個設計中,每當我們需要添加新的圖形類型,就需要修改 GraphicEditor 類,添加新的 if 條件。我們需要修改已有的代碼來實現(xiàn)新功能。
這就違背了開閉原則-對擴展開發(fā),對修改關閉。增加一個類型,修改了三處代碼。
6.2 開閉原則演變
為了符合開閉原則,我們可以進行重構。首先,我們定義一個抽象類Shape
:
public class GraphicEditor {public void draw(Shape shape) {shape.draw();}interface Shape {void draw();}class Rectangle implements Shape {@Overridepublic void draw() {System.out.println("畫矩形");}}class Circle implements Shape {@Overridepublic void draw() {System.out.println("畫圓形");}}}
各種類型的形狀自己規(guī)范自己的行為, 而GraphicEditor.draw()只負責畫出來. 當增加一種類型三角形. 只需要
- 第一,增加一個三角形的類,實現(xiàn)Shape接口
- 第二,調(diào)用draw方法,劃出來就可以了
整個過程都是在擴展,而沒有修改原來的類。這個設計是符合開閉原則的。
6.3 使用例子分析
讓我們來看一個具體的使用例子,展示如何遵循開閉原則來進行擴展。
public class Main {public static void main(String[] args) {Shape circle = new Circle();Shape rectangle = new Rectangle();GraphicEditor editor = new GraphicEditor();editor.drawShape(circle);editor.drawShape(rectangle);}
}
在這個例子中,我們創(chuàng)建了一個圓形對象和一個矩形對象,并通過 GraphicEditor 類來繪制它們。當我們需要添加新的圖形類型(例如三角形)時,只需創(chuàng)建一個新的類實現(xiàn) Shape 接口,而不需要修改現(xiàn)有的代碼:
// 三角形類
public class Triangle implements Shape {@Overridepublic void draw() {// 繪制三角形的代碼}
}// 使用新的三角形類
public class Main {public static void main(String[] args) {Shape circle = new Circle();Shape rectangle = new Rectangle();Shape triangle = new Triangle();GraphicEditor editor = new GraphicEditor();editor.drawShape(circle);editor.drawShape(rectangle);editor.drawShape(triangle);}
}
通過這種方式,我們可以在不修改 GraphicEditor 類的情況下,輕松地擴展新的圖形類型,真正實現(xiàn)了對擴展開放,對修改關閉的設計原則。
07.銀行業(yè)務案例分析
7.1 業(yè)務基礎實現(xiàn)
比如現(xiàn)在有一個銀行業(yè)務,存錢,取錢和轉賬。最初我們會怎么思考呢?
- 首先有一個銀行業(yè)務類, 用來處理銀行的業(yè)務
- 銀行有哪些業(yè)務呢? 存錢,取錢,轉賬, 這都是銀行要執(zhí)行的操作
- 那外部說我要存錢, 我要取錢,我要轉賬, 通過一個類型來告訴我們
public class BankBusiness {public void operate(int type) {if (type == 1) {save();} else if(type == 2) {take();} else if(type == 3) {transfer();}}public void save(){System.out.println("存錢");}public void take(){System.out.println("取錢");}public void transfer() {System.out.println("轉賬");}
}
咋一看已經(jīng)實現(xiàn)了需求. 但是現(xiàn)在有新的需求來了, 銀行要增加功能—理財. 理財是銀行業(yè)務的一種, 自然是新增一個方法.
然后在operate()方法里增加一種類型. 這就是一個糟糕的設計, 增加新功能, 但是卻修改了原來的代碼
7.2 開閉原則演變
設計成接口抽象的形式,利用開關原則,可以嘗試改造為下面的代碼。將不同對象分類的服務方法進行抽象,把業(yè)務邏輯的緊耦合關系拆開,實現(xiàn)代碼的隔離保證了方便的擴展。
public interface Business {public void operate();
}public class Save implements Business {@Overridepublic void operate() {System.out.println("存錢業(yè)務");}
}public class Take implements Business {@Overridepublic void operate() {System.out.println("取錢業(yè)務");}
}public class Transfer implements Business {@Overridepublic void operate() {System.out.println("轉賬業(yè)務");}
}public class BankBusinesses {/*** 處理銀行業(yè)務* @param business*/public void operate(Business business) {System.out.println("處理銀行業(yè)務");business.operate();}
}
通過接口抽象的形式方便擴展, 加入要新增理財功能. 只需新增一個理財類, 其他業(yè)務代碼都不需要修改.
其實, 在日常工作中, 經(jīng)常會遇到這種情況. 因為我們平時寫業(yè)務邏輯會更多一些, 而業(yè)務就像流水賬, 今天一個明天一個一點一點的增加. 所以,當業(yè)務增加到3個的時候, 我們就要思考, 如何寫能夠方便擴展
08.開閉原則優(yōu)缺點
8.1 開閉原則的優(yōu)點
- 提高了代碼的可維護性與復用性:遵循開閉原則可以讓代碼更加穩(wěn)定和可維護,同時也使得代碼更容易被復用。如果我們需要修改某個模塊的行為,只需要擴展該模塊而不需要直接修改源代碼,這樣就不會影響到其他的模塊。
- 提高了代碼的可擴展性:開閉原則還可以提高代碼的可擴展性。通過擴展已有的代碼,我們可以很容易地添加新的功能或改進現(xiàn)有功能,從而適應業(yè)務需求的更改。
- 提高了代碼的可測試性:遵循開閉原則可以降低代碼的耦合度,使得測試更加容易。因為我們只需要測試新增的代碼,而不必驗證已有代碼的正確性。
8.2 開閉原則的缺點
- 對代碼的設計要求高:遵循開閉原則需要對代碼進行良好的抽象和封裝,這對程序員的設計能力和經(jīng)驗要求較高。如果設計不好,可能會導致代碼過于復雜難以維護。
- 可能會增加代碼量:通過擴展已有的代碼來實現(xiàn)新功能,可能會增加代碼量,使得系統(tǒng)變得更加復雜。這需要我們在平衡可維護性和代碼量之間做出權衡。
- 可能會帶來設計上的限制:在某些情況下,為了遵循開閉原則,我們可能需要引入更多的抽象層或接口。這可能會帶來一定的設計上的限制,限制了代碼的表達能力和靈活性。
09.開閉原則的總結
9.1 理解開閉原則
如何理解“對擴展開放、對修改關閉”?
添加一個新的功能,應該是通過在已有代碼基礎上擴展代碼(新增模塊、類、方法、屬性等),而非修改已有代碼(修改模塊、類、方法、屬性等)的方式來完成。
關于定義,我們有兩點要注意。
- 第一點是,開閉原則并不是說完全杜絕修改,而是以最小的修改代碼的代價來完成新功能的開發(fā)。
- 第二點是,同樣的代碼改動,在粗代碼粒度下,可能被認定為“修改”;在細代碼粒度下,可能又被認定為“擴展”。
9.2 如何做到開閉原則
如何做到“對擴展開放、修改關閉”?
我們要時刻具備擴展意識、抽象意識、封裝意識。在寫代碼的時候,我們要多花點時間思考一下,這段代碼未來可能有哪些需求變更,如何設計代碼結構,事先留好擴展點,以便在未來需求變更的時候,在不改動代碼整體結構、做到最小代碼改動的情況下,將新的代碼靈活地插入到擴展點上。
學習設計原則,要多問個為什么。
不能把設計原則當真理,而是要理解設計原則背后的思想。搞清楚這個,比單純理解原則講的是啥,更能讓你靈活應用原則。
9.3 開閉原則的總結
- 問題思考:開閉原則的主要用途是什么?如何才能做到對外拓展開放,對內(nèi)修改關閉?你平常是如何理解開閉原則的,判斷的標準是什么?
- 如何理解開閉原則:軟件實體(模塊、類、方法等)應該“對擴展開放、對修改關閉”。
- 為何開閉原則比較難學:怎樣的代碼改動才被定義為‘修改’?怎么才算滿足或違反‘開閉原則’?如何理解大部分設計模式都是遵循開閉原則!
- 開筆原則的背景:軟件/業(yè)務迭代升級中,面對代碼變化,修改原來代碼可能引入原有模塊bug風險,因此盡量通過對外拓展來實現(xiàn)功能迭代。
- 實現(xiàn)開閉原則的方式:常用的設計技術有以下幾種,1.抽象類和接口;2.策略模式;3.裝飾器模式等。
- 開閉原則的案例教學:繪制圓形/矩形,通過if-else來執(zhí)行不同case邏輯,新增一種類型則需要修改原邏輯。因此考慮通過接口+抽象類方式實現(xiàn)友好拓展。
- 開閉原則的優(yōu)點:1.提高代碼拓展性和可維護性;2.提高代碼可測試的粒度;3.降低的代碼耦合度。
- 開閉原則的缺點:1.對代碼設計要求高;2.可能會導致代碼量增大和變得復雜;3.可能會帶來設計上的限制。
- 總結如何理解開閉原則:當需求發(fā)生變化時,我們應該通過添加新的代碼來擴展功能,而不是修改已有的代碼。
- 總結如何運用開閉原則:通過封裝變化、使用抽象化、利用擴展點和遵循依賴倒置原則來實現(xiàn)。
10.更多內(nèi)容推薦
模塊 | 描述 | 備注 |
---|---|---|
GitHub | 多個YC系列開源項目,包含Android組件庫,以及多個案例 | GitHub |
博客匯總 | 匯聚Java,Android,C/C++,網(wǎng)絡協(xié)議,算法,編程總結等 | YCBlogs |
設計模式 | 六大設計原則,23種設計模式,設計模式案例,面向對象思想 | 設計模式 |
Java進階 | 數(shù)據(jù)設計和原理,面向對象核心思想,IO,異常,線程和并發(fā),JVM | Java高級 |
網(wǎng)絡協(xié)議 | 網(wǎng)絡實際案例,網(wǎng)絡原理和分層,Https,網(wǎng)絡請求,故障排查 | 網(wǎng)絡協(xié)議 |
計算機原理 | 計算機組成結構,框架,存儲器,CPU設計,內(nèi)存設計,指令編程原理,異常處理機制,IO操作和原理 | 計算機基礎 |
學習C編程 | C語言入門級別系統(tǒng)全面的學習教程,學習三到四個綜合案例 | C編程 |
C++編程 | C++語言入門級別系統(tǒng)全面的教學教程,并發(fā)編程,核心原理 | C++編程 |
算法實踐 | 專欄,數(shù)組,鏈表,棧,隊列,樹,哈希,遞歸,查找,排序等 | Leetcode |
Android | 基礎入門,開源庫解讀,性能優(yōu)化,Framework,方案設計 | Android |
23種設計模式
23種設計模式 & 描述 & 核心作用 | 包括 |
---|---|
創(chuàng)建型模式 提供創(chuàng)建對象用例。能夠將軟件模塊中對象的創(chuàng)建和對象的使用分離 | 工廠模式(Factory Pattern) 抽象工廠模式(Abstract Factory Pattern) 單例模式(Singleton Pattern) 建造者模式(Builder Pattern) 原型模式(Prototype Pattern) |
結構型模式 關注類和對象的組合。描述如何將類或者對象結合在一起形成更大的結構 | 適配器模式(Adapter Pattern) 橋接模式(Bridge Pattern) 過濾器模式(Filter、Criteria Pattern) 組合模式(Composite Pattern) 裝飾器模式(Decorator Pattern) 外觀模式(Facade Pattern) 享元模式(Flyweight Pattern) 代理模式(Proxy Pattern) |
行為型模式 特別關注對象之間的通信。主要解決的就是“類或對象之間的交互”問題 | 責任鏈模式(Chain of Responsibility Pattern) 命令模式(Command Pattern) 解釋器模式(Interpreter Pattern) 迭代器模式(Iterator Pattern) 中介者模式(Mediator Pattern) 備忘錄模式(Memento Pattern) 觀察者模式(Observer Pattern) 狀態(tài)模式(State Pattern) 空對象模式(Null Object Pattern) 策略模式(Strategy Pattern) 模板模式(Template Pattern) 訪問者模式(Visitor Pattern) |