什么里斯替換原則
定義1:如果對(duì)每一個(gè)類(lèi)型為S的對(duì)象o1,都有類(lèi)型T的對(duì)象o2,在程序P中,使得T定義的o1都能替代成o2,而程序P的行為沒(méi)有發(fā)生變化,那么類(lèi)型S是類(lèi)型T的子類(lèi)型。
定義2:所有應(yīng)用基類(lèi)的地方必須能夠透明地使用其子類(lèi)的對(duì)象。
2個(gè)定義都是可以的,只是第2種讀起來(lái)比較繞口,第2個(gè)比較簡(jiǎn)潔,個(gè)人建議多品一品定義1。
里斯替換規(guī)則
- 其他類(lèi)應(yīng)該依賴(lài)父類(lèi)或接口
//學(xué)生
class Student{
public String name;
}
//老師
class Teacher{
//報(bào)名
public static void signUp(Student student){
System.out.println("當(dāng)前報(bào)名學(xué)生姓名:"+student.name);
}
}
//學(xué)生張三
class ZhangSan extends Student{
}
class TeacherWang{
//報(bào)名
public static void signUp(ZhangSan zhangSan){
System.out.println("當(dāng)前報(bào)名學(xué)生姓名:"+zhangSan.name);
}
}
看看上面的區(qū)別,Teacher依賴(lài)的是Student,而TeacherWang依賴(lài)的是的ZhangSan,可以說(shuō)TeacherWang是不符合LSP原則,當(dāng)然我們?cè)谠O(shè)計(jì)程序的時(shí)候,并不是全部都要使用這個(gè)原則,有的時(shí)候也是直接依賴(lài)子類(lèi)的,這個(gè)要看具體的需求。
- 子類(lèi)必須完全是父類(lèi)的方法
Class Student{
//獲取學(xué)生證
public abstract String getStudentID();
}
Class ZhangSan extends Student{
public String getStudentID(){
return null;
}
}
我們看ZhangSan是Student的子類(lèi),但是沒(méi)有實(shí)現(xiàn)getStudentID()方法,假如學(xué)校門(mén)衛(wèi)是根據(jù)學(xué)生證,讓你走進(jìn)校園,如果沒(méi)有學(xué)生證,那就沒(méi)有資格進(jìn)入校園,那就相當(dāng)于不是學(xué)生。
子類(lèi)可以有不同于父類(lèi)的方法和屬性
ZhangSan、LiSi都是Student,那么他們就有所有Student的屬性和方法,但是ZhangSan喜歡下象棋,LiSi喜歡打籃球,那么這樣也是可以的,那么反過(guò)來(lái)講就不成立了,難道只要是Student都喜歡打籃球和下象棋嗎?重載父類(lèi)的方法,參數(shù)類(lèi)型>=重載參數(shù) 或 參數(shù)類(lèi)型 != 重載參數(shù)
//父類(lèi)
class Parent {
public void test(HashMap hashMap){}
}
//子類(lèi)
class Child extends Parent{
@Override
public void test(HashMap hashMap){}
public void test(Map map){}
public void test(String text){}
}
//程序P
class Test{
public static void main(String[] args){
//使用父類(lèi)
Parent parent = new Parent();
HashMap hashMap = new HashMap();
parent.test(hashMap);
//用子類(lèi)進(jìn)行替換
//Child child = new Child ();
//HashMap hashMap = new HashMap();
//child.test(hashMap);
}
}
//根據(jù)定義1,咱們把Parent換成Child,最終調(diào)用的還是Parent里面的方法。
反之看看成立嗎?
//父類(lèi)
class Parent {
public void test(Map map){}
}
//子類(lèi)
class Child extends Parent{
public void test(HashMap hashMap){}
@Override
public void test(Map map){}
public void test(String text){}
}
//程序P
class Test{
public static void main(String[] args){
//使用父類(lèi)
Parent parent = new Parent();
HashMap hashMap = new HashMap();
parent.test(hashMap);
//用子類(lèi)進(jìn)行替換
//Child child = new Child ();
//HashMap hashMap = new HashMap();
//child.test(hashMap);
}
}
替換之后發(fā)現(xiàn)child調(diào)用的不是父類(lèi)的test(Map map)方法,而是自己的test(HashMap hashMap)方法,不符合定義1,替換之后不印象程序P的功能。
優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
- 共享代碼,減少工作量,子類(lèi)共享父類(lèi)的屬性和方法。
- 提高代碼重用性
- 提高代碼的擴(kuò)展性
缺點(diǎn)
- 集成是侵入性的,只要繼承了父類(lèi),那么就必須擁有父類(lèi)的所有的屬性和方法。
- 降低代碼的靈活性,由于繼承了父類(lèi),那么父類(lèi)就對(duì)子類(lèi)進(jìn)行了約束。
- 增強(qiáng)了耦合性,繼承本來(lái)就是強(qiáng)耦合性的,父類(lèi)修改屬性和方法的時(shí)候,必須需要考慮子類(lèi)的修改。