里氏替換原則
- java OO 中繼承性的思考和說明:
- 繼承包含這樣一層含義:父類中凡是已經(jīng)實(shí)現(xiàn)好的方法,實(shí)際上就是在設(shè)定規(guī)范和契約,雖然它不強(qiáng)制要求所有的子類必須遵循這些契約,但是如果子類對(duì)這些已經(jīng)實(shí)現(xiàn)的方法任意修改,就會(huì)對(duì)這個(gè)繼承體系造成破壞。
- 繼承在給程序設(shè)計(jì)帶來便利的同時(shí),也帶來了弊端。比如使用繼承會(huì)給程序帶來 傾入性,程序的可移植性就會(huì)降低,會(huì)增加對(duì)象間的耦合性,如果一個(gè)類被其他的類所繼承,則當(dāng)這個(gè)類需要修改的時(shí)候,即 當(dāng)父類需要修改的時(shí)候,必須要考慮到子類,因?yàn)榭赡芨割愋薷暮?,所有繼承的子類的功能方法都有可能產(chǎn)生問題故障
- 在實(shí)際編程中,如何正常的使用繼承----- 里氏替換原則
- 里氏替換原則介紹:
- 如果對(duì)每個(gè)類型為 T1 的對(duì)象 O1,都有類型為 T2 的對(duì)象 O2,使得以 T1 定義的所有程序 P 在所有的對(duì)象 O1 都待換成 O2 時(shí),程序 P 的行為沒有發(fā)送變化,那么類型 T2 是類型 T1 的子類型。換句話說,所有引用基類的地方必須能透明的使用其子類的對(duì)象。
- 在使用繼承的時(shí)候,遵循里氏替換原則,在 子類中盡量不要重寫父類的方法
- 里氏替換原則告訴我們,繼承實(shí)際上讓兩個(gè)類 耦合性 增強(qiáng)了 (通俗的說,子類繼承了父類,父類一旦變化了,子類就必須要跟著變了,所以說耦合性增強(qiáng)了),在適當(dāng)?shù)那闆r下,可以通過 聚合,組合,依賴 來解決問題。
案例
- 代碼案例
public class Liskov {
public static void main(String[] args) {
A a = new A();
System.out.println("11-3=" + a.func1(11, 3));
System.out.println("1-8=" + a.func1(1, 8));
System.out.println("-----------------------");
B b = new B();
System.out.println("11-3=" + b.func1(11, 3));//這里本意是求出11-3
System.out.println("1-8=" + b.func1(1, 8));// 1-8
System.out.println("11+3+9=" + b.func2(11, 3));
}
}
// A 類
class A {
// 返回兩個(gè)數(shù)的差
public int func1(int num1, int num2) {
return num1 - num2;
}
}
// B 類繼承了A
// 增加了一個(gè)新功能:完成兩個(gè)數(shù)相加,然后和9 求和
class B extends A {
//這里,重寫了A 類的方法, 可能是無意識(shí)
public int func1(int a, int b) {
return a + b;
}
public int func2(int a, int b) {
return func1(a, b) + 9;
}
}
- 解決方法
- 我們發(fā)現(xiàn)原來正常的減法功能發(fā)生了錯(cuò)誤。原因就是類 B 無意中重寫了父類的方法,造成原有功能出現(xiàn)錯(cuò)誤。在實(shí)際編程中,我們常常會(huì)通過重寫父類的方法完成新的功能,這樣寫起來雖然簡單,但整個(gè)繼承體系的復(fù)用性會(huì)比較差。特別是運(yùn)行多態(tài)比較繁瑣的時(shí)候
- 通用的方法就是: 原有的父類和子類都繼承一個(gè)更加通俗的 BaseClass 基礎(chǔ)類,并且將原本的父類與子類之間的繼承關(guān)系去掉,采用 依賴,聚合,組合 等關(guān)系替代
-
改進(jìn)方案:
父類與子類繼承更加基礎(chǔ)的Base類
改進(jìn)
- 代碼
public class Liskov {
public static void main(String[] args) {
A a = new A();
System.out.println("11-3=" + a.func1(11, 3));
System.out.println("1-8=" + a.func1(1, 8));
System.out.println("-----------------------");
B b = new B();
//因?yàn)锽 類不再繼承A 類,因此調(diào)用者,不會(huì)再func1 是求減法
//調(diào)用完成的功能就會(huì)很明確
System.out.println("11+3=" + b.func1(11, 3));//這里本意是求出11+3
System.out.println("1+8=" + b.func1(1, 8));// 1+8
System.out.println("11+3+9=" + b.func2(11, 3));
//使用組合仍然可以使用到A 類相關(guān)方法
System.out.println("11-3=" + b.func3(11, 3));// 這里本意是求出11-3
}
}
//創(chuàng)建一個(gè)更加基礎(chǔ)的基類
class Base {
//把更加基礎(chǔ)的方法和成員寫到Base 類
}
// A 類繼承 Base 基礎(chǔ)類
class A extends Base {
// 返回兩個(gè)數(shù)的差
public int func1(int num1, int num2) {
return num1 - num2;
}
}
// B 類繼承 Base 基礎(chǔ)類
class B extends Base {
//如果B 需要使用A 類的方法,使用組合關(guān)系
private A a = new A();
//這里,重寫了A 類的方法, 可能是無意識(shí)
public int func1(int a, int b) {
return a + b;
}
public int func2(int a, int b) {
return func1(a, b) + 9;
}
//我們?nèi)匀幌胧褂肁 的方法
public int func3(int a, int b) {
return this.a.func1(a, b);
}
}