開閉原則的定義
開閉原則是java世界里最基礎的設計原則,它指導我們如何建立一個穩定,靈活的系統。開閉原則定義如下:
Software entities like classes,modules and functions should be open for extension but closed for modifications.
一個軟件實體如類,模塊和函數應該對擴展開放,對修改關閉。
為什么要對修改關閉?對擴展開放?
1,軟件生命周期中,將會不停的進行迭代更新。
這個過程中最重要對兩點就是1.保證現有功能穩定 2,保證新需求可以實現
什么是開閉原則
開閉原則明確的告訴我們:軟件實現應該對擴展開放,對修改關閉,其含義是說一個軟件實體應該通過擴展來實現變化,而不是通過修改已有的代碼來實現變化的。那什么是軟件實體呢?軟件實體包括以下幾個部分:
- 項目或軟件產品中按照一定的邏輯規則划分的模塊
- 抽象和類
- 方法
一個軟件產品只要在生命周期內,都會發生變化,即然變化是一個事實,我們就應該在設計時盡量適應這些變化,以提高項目的穩定性和靈活性,真正實現“擁抱變化”。開閉原則告訴我們應盡量通過擴展軟件實體的行為來實現變化,而不是通過修改現有代碼來完成變化,它是為軟件實體的未來事件而制定的對現行開發設計進行約束的一個原則。
我們舉例說明什么是開閉原則,以書店銷售書籍為例,其類圖如下:

書籍接口:
public interface IBook{ public String getName(); public String getPrice(); public String getAuthor(); }
小說類書籍:
public class NovelBook implements IBook{ private String name; private int price; private String author; public NovelBook(String name,int price,String author){ this.name = name; this.price = price; this.author = author; } public String getAutor(){ return this.author; } public String getName(){ return this.name; } public int getPrice(){ return this.price; } }
Client類:
public class Client{ public static void main(Strings[] args){ IBook novel = new NovelBook("笑傲江湖",100,"金庸"); System.out.println("書籍名字:"+novel.getName()+"書籍作者:"+novel.getAuthor()+"書籍價格:"+novel.getPrice()); } }
項目投產生,書籍正常銷售,但是我們經常因為各種原因,要打折來銷售書籍,這是一個變化,我們要如何應對這樣一個需求變化呢?
我們有下面三種方法可以解決此問題:
-
修改接口
在IBook接口中,增加一個方法getOffPrice(),專門用於進行打折處理,所有的實現類實現此方法。但是這樣的一個修改方式,實現類NovelBook要修改,同時IBook接口應該是穩定且可靠,不應該經常發生改變,否則接口作為契約的作用就失去了。因此,此方案否定。 -
修改實現類
修改NovelBook類的方法,直接在getPrice()方法中實現打折處理。此方法是有問題的,例如我們如果getPrice()方法中只需要讀取書籍的打折前的價格呢?這不是有問題嗎?當然我們也可以再增加getOffPrice()方法,這也是可以實現其需求,但是這就有二個讀取價格的方法,因此,該方案也不是一個最優方案。 -
通過擴展實現變化
我們可以增加一個子類OffNovelBook,覆寫getPrice方法。此方法修改少,對現有的代碼沒有影響,風險少,是個好辦法。下面是修改后的類圖:

打折類:
public class OffNovelBook extends NovelBook{ public OffNovelBook(String name,int price,String author){ super(name,price,author); } //覆寫價格方法,當價格大於40,就打8析,其他價格就打9析 public int getPrice(){ if(this.price > 40){ return this.price * 0.8; }else{ return this.price * 0.9; } } }
現在打折銷售開發完成了,我們只是增加了一個OffNovelBook類,我們修改的代碼都是高層次的模塊,沒有修改底層模塊,代碼改變量少,可以有效的防止風險的擴散。
我們可以把變化歸納為二種類型:
-
邏輯變化
只變化了一個邏輯,而不涉及其他模塊,比如一個算法是abc,現在需要修改為a+b+c,可以直接通過修改原有類中的方法的方式來完成,前提條件是所有依賴或關聯類都按照相同的邏輯處理 -
子模塊變化
一人模塊變化,會對其它的模塊產生影響,特別是一個低層次的模塊變化必然引起高層模塊的變化,因此在通過擴展完成變化。
為什么使用開閉原則
第一:開閉原則非常有名,只要是面向對象編程,在開發時都會強調開閉原則
第二:開閉原則是最基礎的設計原則,其它的五個設計原則都是開閉原則的具體形態,也就是說其它的五個設計原則是指導設計的工具和方法,而開閉原則才是其精神領袖。依照java語言的稱謂,開閉原則是抽象類,而其它的五個原則是具體的實現類。
第三:開閉原則可以提高復用性
在面向對象的設計中,所有的邏輯都是從原子邏輯組合而來,不是在一個類中獨立實現一個業務邏輯。只有這樣的代碼才可以復用,粒度越小,被復用的可能性越大。那為什么要復用呢?減少代碼的重復,避免相同的邏輯分散在多個角落,減少維護人員的工作量。那怎么才能提高復用率呢?縮小邏輯粒度,直到一個邏輯不可以分為止。
第四:開閉原則可以提高維護性
一款軟件量產后,維護人員的工作不僅僅對數據進行維護,還可能要對程序進行擴展,維護人員最樂意的事是擴展一個類,而不是修改一個類。讓維護人員讀懂原有代碼,再進行修改,是一件非常痛苦的事情,不要讓他在原有的代碼海洋中游盪后再修改,那是對維護人員的折磨和摧殘。
第五:面向對象開發的要求
萬物皆對象,我們要把所有的事物抽象成對象,然后針對對象進行操作,但是萬物皆發展變化,有變化就要有策略去應對,怎么快速應對呢?這就需要在設計之初考慮到所有可能變化的因素,然后留下接口,等待“可能”轉變為“現實”。
如何使用開閉原則
第一:抽象約束
抽象是對一組事物的通用描述,沒有具體的實現,也就表示它可以有非常多的可能性,可以跟隨需求的變化而變化。因此,通過接口或抽象類可以約束一組可能變化的行為,並且能夠實現對擴展開放,其包含三層含義:
- 通過接口或抽象類約束擴散,對擴展進行邊界限定,不允許出現在接口或抽象類中不存在的public方法。
- 參數類型,引用對象盡量使用接口或抽象類,而不是實現類,這主要是實現里氏替換原則的一個要求
- 抽象層盡量保持穩定,一旦確定就不要修改
第二:元數據(metadata)控件模塊行為
編程是一個很苦很累的活,那怎么才能減輕壓力呢?答案是盡量使用元數據來控制程序的行為,減少重復開發。什么是元數據?用來描述環境和數據的數據,通俗的說就是配置參數,參數可以從文件中獲得,也可以從數據庫中獲得。
第三:制定項目章程
在一個團隊中,建立項目章程是非常重要的,因為章程是所有人員都必須遵守的約定,對項目來說,約定優於配置。這比通過接口或抽象類進行約束效率更高,而擴展性一點也沒有減少。
第四:封裝變化
對變化封裝包含兩層含義:
(1) 將相同的變化封裝到一個接口或抽象類中
(2) 將不同的變化封裝到不同的接口或抽象類中,不應該有兩個不同的變化出現在同一個接口或抽象類中。
封裝變化,也就是受保護的變化,找出預計有變化或不穩定的點,我們為這些變化點創建穩定的接口。
作者:WILL_HUNTING
鏈接:https://www.jianshu.com/p/d36da4f136c4
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。