基本知識
1.核心思想:面向抽象編程
2.基本內涵:對修改關閉,對擴展開放
3.要求:盡可能不修改源碼而是增加新功能
例子
以spring5核心原理與30個類手寫實戰中的為例
package com.gupaoedu.vip.design.principle.openclose;/*** Created by Tom*/
public interface ICourse {Integer getId();String getName();Double getPrice();
}
package com.gupaoedu.vip.design.principle.openclose;/*** Created by Tom*/
public class JavaCourse implements ICourse{private Integer Id;private String name;private Double price;public JavaCourse(Integer id, String name, Double price) {this.Id = id;this.name = name;this.price = price;}public Integer getId() {return this.Id;}public String getName() {return this.name;}public Double getPrice() {return this.price;}}
package com.gupaoedu.vip.design.principle.openclose;/*** Created by Tom*/
public class JavaDiscountCourse extends JavaCourse {public JavaDiscountCourse(Integer id, String name, Double price) {super(id, name, price);}public Double getDiscountPrice(){return super.getPrice() * 0.61;}// public Double getOriginPrice(){
// return super.getPrice();
// }
//
// public Double getPrice(){
// return super.getPrice() * 0.61;
// }}
package com.gupaoedu.vip.design.principle.openclose;/*** Created by Tom.*/
public class OpenCloseTest {public static void main(String[] args) {ICourse iCourse = new JavaDiscountCourse(232, "【咕泡學院】Java架構師專題課", 11800D);JavaDiscountCourse javaCourse = (JavaDiscountCourse) iCourse;System.out.println("課程ID:" + javaCourse.getId() +"\n課程名稱:《" + javaCourse.getName() + "》" +"\n原價:" + javaCourse.getPrice() + "元" +"\n折后價:" + javaCourse.getDiscountPrice() + "元");// ICourse iCourse = new JavaDiscountCourse(232, "【咕泡學院】Java架構師專題課", 11800D);
// JavaDiscountCourse javaCourse = (JavaDiscountCourse) iCourse;
// System.out.println("課程ID:" + javaCourse.getId() +
// "\n課程名稱:《" + javaCourse.getName() + "》" +
// "\n原價:" + javaCourse.getOriginPrice() + "元" +
// "\n折后價:" + javaCourse.getPrice() + "元");}
}
這里的javaDiscountCourse就是繼承了javaCourse,實現功能拓展,新增了一個折扣功能
至于注釋掉的getPrice()方法
// public Double getPrice(){
// return super.getPrice() * 0.61;
// }
也是符合開閉原則的,因為它通過重寫父類方法來改變行為,而沒有修改父類。在這種情況下,JavaDiscountCourse 就不僅僅是拓展,而是改變了父類行為。
不過具體場景推薦使用getDiscountPrice(),因為職責單一清晰,getPrice()方法表示原始價格,getDisountPrice表示折扣價格,職責分離,代碼更清晰易懂
,同時也可以避免歧義,getPrice()如果也可能獲得打折價格,則可能讓調用者混亂。
總體上:JavaDiscountCourse 通過繼承并新增方法的方式,在不修改 JavaCourse 現有代碼的基礎上,實現了獲取打折價格的功能,完美體現了開閉原則的精神
對于測試類中的方法:
JavaDiscountCourse javaCourse = (JavaDiscountCourse) iCourse; // 這里進行了向下轉型
這里進行了向下轉型,為了調用 JavaDiscountCourse 特有的 getDiscountPrice() 方法,不得不將 ICourse 類型的引用強制轉換為 JavaDiscountCourse 類型。缺點來說,一方面破壞了原本的多態,畢竟使用javaDiscountCourse賦值給ICourse目的是為了使用多態,但是向下轉型則退化了這種優勢。而且也可能導致ClassCastException,需要判斷對象是不是對應的類的實例。此外,getDiscountPrice()方法緊耦合于類javaDiscountCourse,不利于統一管理。
我們借鑒之前注釋的getPrice方法,既然獲取價格這種需求是很常見的,那么我們自然可以抽象到接口中。我們為了避免混淆,可以換個名字:
比如getActualPrice(),即獲取實際價格
public interface ICourse {Integer getId();String getName();Double getPrice(); // 原始價格Double getActualPrice(); // 新增方法:獲取實際支付價格 (可能是原價,也可能是優惠價)
}public class JavaCourse implements ICourse {// ...@Overridepublic Double getActualPrice() {return getPrice(); // 對于原價課程,實際價格就是原價}
}public class JavaDiscountCourse extends JavaCourse {// ...@Overridepublic Double getActualPrice() {return super.getPrice() * 0.61; // 對于打折課程,實際價格是折后價}
}
這樣,在 OpenCloseTest 中,只需要:
ICourse iCourse = new JavaDiscountCourse(232, "【咕泡學院】Java架構師專題課", 11800D);
System.out.println("課程ID:" + iCourse.getId() +"\n課程名稱:《" + iCourse.getName() + "》" +"\n原價:" + iCourse.getPrice() + "元" +"\n實際支付價:" + iCourse.getActualPrice() + "元");
// 這里就不需要向下轉型了,因為 getActualPrice() 是接口方法