實例講解Java設(shè)計模式編程中的OCP開閉原則
定義:一個軟件實體如類、模塊和函數(shù)應(yīng)該對擴(kuò)展開放,對修改關(guān)閉。
問題由來:在軟件的生命周期內(nèi),因為變化、升級和維護(hù)等原因需要對軟件原有代碼進(jìn)行修改時,可能會給舊代碼中引入錯誤,也可能會使我們不得不對整個功能進(jìn)行重構(gòu),并且需要原有代碼經(jīng)過重新測試。
解決方案:當(dāng)軟件需要變化時,盡量通過擴(kuò)展軟件實體的行為來實現(xiàn)變化,而不是通過修改已有的代碼來實現(xiàn)變化。
開閉原則是面向?qū)ο笤O(shè)計中最基礎(chǔ)的設(shè)計原則,它指導(dǎo)我們?nèi)绾谓⒎€(wěn)定靈活的系統(tǒng)。開閉原則可能是設(shè)計模式六項原則中定義最模糊的一個了,它只告訴我們對擴(kuò)展開放,對修改關(guān)閉,可是到底如何才能做到對擴(kuò)展開放,對修改關(guān)閉,并沒有明確的告訴我們。以前,如果有人告訴我“你進(jìn)行設(shè)計的時候一定要遵守開閉原則”,我會覺的他什么都沒說,但貌似又什么都說了。因為開閉原則真的太虛了。
在仔細(xì)思考以及仔細(xì)閱讀很多設(shè)計模式的文章后,終于對開閉原則有了一點(diǎn)認(rèn)識。其實,我們遵循設(shè)計模式前面5大原則,以及使用23種設(shè)計模式的目的就是遵循開閉原則。也就是說,只要我們對前面5項原則遵守的好了,設(shè)計出的軟件自然是符合開閉原則的,這個開閉原則更像是前面五項原則遵守程度的“平均得分”,前面5項原則遵守的好,平均分自然就高,說明軟件設(shè)計開閉原則遵守的好;如果前面5項原則遵守的不好,則說明開閉原則遵守的不好。
其實筆者認(rèn)為,開閉原則無非就是想表達(dá)這樣一層意思:用抽象構(gòu)建框架,用實現(xiàn)擴(kuò)展細(xì)節(jié)。因為抽象靈活性好,適應(yīng)性廣,只要抽象的合理,可以基本保持軟件架構(gòu)的穩(wěn)定。而軟件中易變的細(xì)節(jié),我們用從抽象派生的實現(xiàn)類來進(jìn)行擴(kuò)展,當(dāng)軟件需要發(fā)生變化時,我們只需要根據(jù)需求重新派生一個實現(xiàn)類來擴(kuò)展就可以了。當(dāng)然前提是我們的抽象要合理,要對需求的變更有前瞻性和預(yù)見性才行。
在開閉原則的定義中,軟件實體可以指一個軟件模塊、一個由多個類組成的局部結(jié)構(gòu)或一個獨(dú)立的類。
任何軟件都需要面臨一個很重要的問題,即它們的需求會隨時間的推移而發(fā)生變化。當(dāng)軟件系統(tǒng)需要面對新的需求時,我們應(yīng)該盡量保證系統(tǒng)的設(shè)計框架是穩(wěn)定的。如果一個軟件設(shè)計符合開閉原則,那么可以非常方便地對系統(tǒng)進(jìn)行擴(kuò)展,而且在擴(kuò)展時無須修改現(xiàn)有代碼,使得軟件系統(tǒng)在擁有適應(yīng)性和靈活性的同時具備較好的穩(wěn)定性和延續(xù)性。隨著軟件規(guī)模越來越大,軟件壽命越來越長,軟件維護(hù)成本越來越高,設(shè)計滿足開閉原則的軟件系統(tǒng)也變得越來越重要。
為了滿足開閉原則,需要對系統(tǒng)進(jìn)行抽象化設(shè)計,抽象化是開閉原則的關(guān)鍵。在Java、C#等編程語言中,可以為系統(tǒng)定義一個相對穩(wěn)定的抽象層,而將不同的實現(xiàn)行為移至具體的實現(xiàn)層中完成。在很多面向?qū)ο缶幊陶Z言中都提供了接口、抽象類等機(jī)制,可以通過它們定義系統(tǒng)的抽象層,再通過具體類來進(jìn)行擴(kuò)展。如果需要修改系統(tǒng)的行為,無須對抽象層進(jìn)行任何改動,只需要增加新的具體類來實現(xiàn)新的業(yè)務(wù)功能即可,實現(xiàn)在不修改已有代碼的基礎(chǔ)上擴(kuò)展系統(tǒng)的功能,達(dá)到開閉原則的要求。
Sunny軟件公司開發(fā)的CRM系統(tǒng)可以顯示各種類型的圖表,如餅狀圖和柱狀圖等,為了支持多種圖表顯示方式,原始設(shè)計方案如下圖所示:

在ChartDisplay類的display()方法中存在如下代碼片段:
......
if (type.equals("pie")) {
PieChart chart = new PieChart();
chart.display();
}
else if (type.equals("bar")) {
BarChart chart = new BarChart();
chart.display();
}
......
在該代碼中,如果需要增加一個新的圖表類,如折線圖LineChart,則需要修改ChartDisplay類的display()方法的源代碼,增加新的判斷邏輯,違反了開閉原則。
現(xiàn)對該系統(tǒng)進(jìn)行重構(gòu),使之符合開閉原則。
在本實例中,由于在ChartDisplay類的display()方法中針對每一個圖表類編程,因此增加新的圖表類不得不修改源代碼??梢酝ㄟ^抽象化的方式對系統(tǒng)進(jìn)行重構(gòu),使之增加新的圖表類時無須修改源代碼,滿足開閉原則。具體做法如下:
(1) 增加一個抽象圖表類AbstractChart,將各種具體圖表類作為其子類;
(2) ChartDisplay類針對抽象圖表類進(jìn)行編程,由客戶端來決定使用哪種具體圖表。
重構(gòu)后結(jié)構(gòu)如下圖所示:

在圖2中,我們引入了抽象圖表類AbstractChart,且ChartDisplay針對抽象圖表類進(jìn)行編程,并通過setChart()方法由客戶端來設(shè)置實例化的具體圖表對象,在ChartDisplay的display()方法中調(diào)用chart對象的display()方法顯示圖表。如果需要增加一種新的圖表,如折線圖LineChart,只需要將LineChart也作為AbstractChart的子類,在客戶端向ChartDisplay中注入一個LineChart對象即可,無須修改現(xiàn)有類庫的源代碼。
注意:因為xml和properties等格式的配置文件是純文本文件,可以直接通過VI編輯器或記事本進(jìn)行編輯,且無須編譯,因此在軟件開發(fā)中,一般不把對配置文件的修改認(rèn)為是對系統(tǒng)源代碼的修改。如果一個系統(tǒng)在擴(kuò)展時只涉及到修改配置文件,而原有的Java代碼或C#代碼沒有做任何修改,該系統(tǒng)即可認(rèn)為是一個符合開閉原則的系統(tǒng)。
相關(guān)文章
tomcat目錄結(jié)構(gòu)簡介_動力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了tomcat目錄結(jié)構(gòu)簡介_動力節(jié)點(diǎn)Java學(xué)院整理的相關(guān)資料,需要的朋友可以參考下2017-07-07
Java中通過ZipOutputStream類如何將多個文件打成zip
ZipOutputStream?是Java中用于創(chuàng)建ZIP文件的類,它是?java.util.zip?包中的一部分,通過使用?ZipOutputStream?,可以將多個文件壓縮到一個ZIP文件中,這篇文章主要介紹了Java中(ZipOutputStream)如何將多個文件打成zip,需要的朋友可以參考下2023-09-09
使用maven方式創(chuàng)建springboot項目的方式
使用Spring Initializr創(chuàng)建spring boot項目,因為外網(wǎng)問題導(dǎo)致很難成功,所以只能使用maven方式,這里介紹下使用maven方式創(chuàng)建springboot項目的方法,感興趣的朋友一起看看吧2022-09-09
SpringBoot+隨機(jī)鹽值+雙重MD5實現(xiàn)加密登錄
數(shù)據(jù)加密在很多項目上都可以用到,大部分都會采用MD5進(jìn)行加密,本文主要介紹了SpringBoot+隨機(jī)鹽值+雙重MD5實現(xiàn)加密登錄,具有一定的參考價值,感興趣的可以了解一下2024-02-02
RocketMQ設(shè)計之故障規(guī)避機(jī)制
這篇文章主要介紹了RocketMQ設(shè)計之故障規(guī)避機(jī)制,故障規(guī)避機(jī)制就是用來解決當(dāng)Broker出現(xiàn)故障,Producer不能及時感知而導(dǎo)致消息發(fā)送失敗的問題,下面詳細(xì)介紹需要的小伙伴可以參考一下2022-03-03

