我的博客原文:
開閉原則java
設計模式六大原則之六:開閉原則。git
姓名 :開閉原則github
英文名 :Open Closed Principle設計模式
價值觀 :老頑童就是我,休想改變我框架
我的介紹 :ide
Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.(軟件中的對象(類,模塊,函數等等)應該對於擴展是開放的,可是對於修改是封閉的)
(來自維基百科)函數
停更了三四天了,這幾天比較忙,不只僅是工做上,更可能是精神上。週日忽然老胃病又復發了,一直疼到凌晨 4,5 點。由於此次疼得蠻厲害的,因此準備去醫院看一下醫生,這時候才體驗到大城市就醫之苦。週日晚下載了微醫 App (不是作廣告哈),也不知道哪家醫院好,在深圳兩年半還沒去過醫院,隨便選個三甲醫院:北京大學深圳醫院,看了消化內科門診的醫生列表,整整這一週主任醫生都預定滿了,頓時很崩潰,打電話給醫院預定,最快只能預定 17 號,are you kidding?App 上有個 『當即問診』功能,在線把情況告訴醫生,醫生一天以內接診,須要花 60 塊,我就嘗試一下,沒想到次日醫生回覆後,說能夠下午去醫院看,他能夠臨時加號。就這樣跳過了預定,直接看病,不知道你是否也苦於看病煩,能夠嘗試這個方法,固然,若是你有更好的方法,能夠留言讓更多的人瞭解到。測試
跑題了跑題了,今天是想和你們分享設計模式最後一個原則:開閉原則。這個原則要求就是容許擴展,拒絕修改。既然上面講到看醫生,那就用一個跟看病有關的例子。this
小明去醫院看病,醫生開了阿司匹林藥,小明去了收費臺,付了錢,總共 20 塊錢。例子的代碼以下:debug
public class OcpTest { public static void main(String[] args) { Hospital hospital = new Hospital(); IPatient xiaoMing = new Patient("小明"); hospital.sellMedicine(xiaoMing); } } class Medicine { private String name; private BigDecimal price; public Medicine(String name, BigDecimal price) { this.name = name; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public BigDecimal getPrice() { return price; } public void setPrice(BigDecimal price) { this.price = price; } } class Hospital { private Medicine medicine = new Medicine("阿司匹林", new BigDecimal(20)); public void sellMedicine(IPatient patient) { BigDecimal money = patient.pay(medicine); System.out.println(patient.getName() + " 花了 " + money.setScale(2, BigDecimal.ROUND_UP) + " 塊錢買了藥:" + medicine.getName()); } } interface IPatient { String getName(); BigDecimal pay(Medicine medicine); } class Patient implements IPatient{ private String name; public Patient(String name) { this.name = name; } @Override public BigDecimal pay(Medicine medicines) { return medicines.getPrice(); } @Override public String getName() { return name; } }
次日和朋友聚會聊起這事,小紅說道:不對呀,前幾天我在醫院也拿了阿司匹林藥,才 14 塊錢呢。小花說:奇怪了,我買的是 16 塊錢。小杰迴應:怎麼我買的是 18 塊。怎麼這藥這麼多個價格。小明 Google 搜了一下,發現價格跟社保有關,幾我的便發現,原來他們都是「不一樣人」:小明沒有社保,小紅社保是一檔,小花社保是二擋,小杰社保是三擋。(假設社保一檔打 7 折,社保二擋打 8 折,社保三擋打 9 折,虛擬的哈)
發現了這祕密後,做爲和 IT 工做相關的人,便討論起醫院系統具體實現是怎麼實現的。小紅說:這很簡單呢,藥品給不一樣人提供不一樣的價格。代碼以下:
class Medicine { private String name; private BigDecimal price; public Medicine(String name, BigDecimal price) { this.name = name; this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public BigDecimal getPrice() { return price; } public BigDecimal getPrice1() { return price.multiply(new BigDecimal(0.7)); } public BigDecimal getPrice2() { return price.multiply(new BigDecimal(0.8)); } public BigDecimal getPrice3() { return price.multiply(new BigDecimal(0.9)); } public void setPrice(BigDecimal price) { this.price = price; } }
小花說:藥片自己的價格是不會變的,只是給不一樣人不一樣價格,因此能夠在病人獲取價錢的時候去區分。代碼以下:
class Patient implements IPatient{ private String name; private int level; public Patient(String name) { this.name = name; } @Override public BigDecimal pay(Medicine medicines) { if (level == 1) { return medicines.getPrice().multiply(new BigDecimal(0.7)); } else if (level == 2) { return medicines.getPrice().multiply(new BigDecimal(0.8)); } else if (level == 3) { return medicines.getPrice().multiply(new BigDecimal(0.9)); } return medicines.getPrice(); } @Override public String getName() { return name; } }
小杰陷入了沉思。。。
小明發話:大家說的方法均可以實現,可是總感受不對勁,若是之後有社保四擋,仍是要修改原來的代碼,前 2 天設計模式老師講的開閉原則忘記了麼?裏面說要對擴展開放,對修改封閉。我以爲這個藥片價格是由於咱們人而變的,那是否是咱們能夠把沒社保的歸爲一類人,一檔社保的也爲一類,以此類推。我以爲這樣實現更好,增長多 3 類病人,分別是一檔社保、二擋社保、三擋社保。代碼以下:
class OneLevelSocialSecurityPatient implements IPatient { private String name; public OneLevelSocialSecurityPatient(String name) { this.name = name; } @Override public BigDecimal pay(Medicine medicine) { return medicine.getPrice().multiply(new BigDecimal(0.7)); } @Override public String getName() { return this.name; } } class TwoLevelSocialSecurityPatient implements IPatient { private String name; public TwoLevelSocialSecurityPatient(String name) { this.name = name; } @Override public BigDecimal pay(Medicine medicine) { return medicine.getPrice().multiply(new BigDecimal("0.8")); } @Override public String getName() { return this.name; } } class ThreeLevelSocialSecurityPatient implements IPatient { private String name; public ThreeLevelSocialSecurityPatient(String name) { this.name = name; } @Override public BigDecimal pay(Medicine medicine) { return medicine.getPrice().multiply(new BigDecimal("0.9")); } @Override public String getName() { return this.name; } } // 測試代碼 public static void main(String[] args) { Hospital hospital = new Hospital(); IPatient xiaoMing = new Patient("小明"); hospital.sellMedicine(xiaoMing); IPatient xiaoHong = new OneLevelSocialSecurityPatient("小紅"); hospital.sellMedicine(xiaoHong); IPatient xiaoHua = new TwoLevelSocialSecurityPatient("小花"); hospital.sellMedicine(xiaoHua); IPatient xiaoJie = new ThreeLevelSocialSecurityPatient("小杰"); hospital.sellMedicine(xiaoJie); }
代碼:
OcpTest.java
看了他們的對話和代碼,是否是能知道哪一種方式更好了?對於小紅來講,她沒理清價格變化的緣由,價格變化不在於藥片;小花理清了,可是實現方式差了點,之後若是新增了四擋社保,她的實現要修改原有的代碼,不符合開閉原則;小明的方法就符合開閉原則,若是新增四擋社保人員,他的方法只須要再額外擴展一個四擋社保人員就能夠,不用動用其餘代碼。
用了這個你們可能不太喜歡的看病的場景來描述這個開閉原則,不要忌諱哈,但願你們都健健康康,遠離醫院。
重申一下:對擴展開放,對修改封閉。若是有同窗常常看一些開源框架源碼就會發現,有不少不少抽象類和接口,debug 進去很繞,其實這些抽象類和接口不少都是爲了擴展用,由於做爲開源框架,不得不實現各類可想象到的方案,而這些都基於開閉原則來實現的。之後有機會也能夠寫一下源碼的文章分享給你們。
參考資料:《大話設計模式》、《Java設計模式》、《設計模式之禪》、《研磨設計模式》、《Head First 設計模式》
這周事情比較多,更新會不及時,週五還要出差去一趟上海,週六回深圳,週日回一趟老家,各類奔波。。。
但願文章對您有所幫助,設計模式系列會持續更新,感興趣的同窗能夠關注公衆號,第一時間獲取文章推送閱讀,也能夠一塊兒交流,交個朋友。