## 里氏替換原則
英文名稱(Liskov Substitution Principle,LSP):
定義:**所有引用基類的地方必須能夠透明地使用其子類的對象**
我的理解: **父類出現的地方也可以用子類來替換,而子類出現的地方父類不一定能替換。**
里氏替換原則的為繼承定義的規范,包括4層含義
**1、子類必須完全實現父類的方法**
**2、子類可以有自己的個性**
**3、覆蓋或實現父類的方法時輸入參數可以被放大**
**4、覆寫或實現父類的方法時輸出結果可以被縮小**
模擬CS游戲
戰士可以拿槍去殺敵人,setGun()給戰士設置不同的槍。killEnemy()射殺敵人。
抽象類,AbstractGun,void shoot()方法
槍類MachineGun(機槍),Rifle(步槍),Handgun(手槍)實現抽象類方法
-----這個類的設計原理是,每種槍都有一個“射擊”的方法,因此抽象出來使用。

~~~
public class Test {
public static void main(String[] args) {
Soldier s = new Soldier();
s.setGun(new Handgun());
s.killEnemy();
}
}
abstract class AbstractGun{
public void shoot(){};
}
/**
* 戰士類具有殺死敵人的方法,設置不同類別的槍的方法
* @author admin
*
*/
class Soldier{
private AbstractGun gun;
public void setGun(AbstractGun gun){
this.gun = gun;
}
public void killEnemy(){
gun.shoot();
System.out.println("正在射殺敵人...");
}
}
class MachineGun extends AbstractGun{
@Override
public void shoot() {
System.out.println("機關槍掃射...");
}
}
class Rifle extends AbstractGun{
@Override
public void shoot() {
System.out.println("步槍射擊...");
}
}
class Handgun extends AbstractGun{
@Override
public void shoot() {
System.out.println("手槍射擊...");
}
}
~~~
## 子類必須完全實現父類的方法
當如果又來了一個玩具槍時,我們一貫的思想是,將玩具槍繼承AbstractGun類,實現shoot方法。
但是依照情景玩具槍不具備射擊的能力。因此玩具槍不能直接繼承AbstractGun類。因為它不能完整的實現父類的方法。

## 子類可以有自己的個性
這句話很好理解,子類繼承父類,不僅擁有父類的方法和屬性,而且自己還可以用于其他的方法和屬性,并且可以覆寫父類的方法,重載父類的方法等。
~~~
<span style="font-size:18px;">public class Test {
public static void main(String[] args) {
/* Soldier s = new Soldier();
s.setGun(new Handgun());
s.killEnemy();
System.out.println("----------------");*/
Snipper juji = new Snipper();
juji.setGun(new AUG());
juji.killEnemy();
}
}
abstract class AbstractGun{
public void shoot(){};
}
class Snipper{
private AUG aug;
public void setGun(AUG gun){
this.aug = gun;
}
public void killEnemy(){
aug.zoomOut();
aug.shoot();
System.out.println("狙擊手正在射殺敵人...");
}
}
class AUG extends Rifle{
@Override
public void shoot() {
System.out.println("狙擊槍射擊...");
}
public void zoomOut(){
System.out.println("狙擊槍正在瞄準");
}
}</span>
~~~
**覆蓋或實現父類的方法時輸入參數可以被放大**
---這句話類似于子類重載父類的方法時,保證子類的傳入參數的范圍大于父類的參數。
如果沒有滿足以上條件的話,就不滿足父類存在的地方子類也存在的條件,因而違背了里氏替換原則。
例如兩個代碼的比較:
~~~
package hpu.lzl.lsp;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class Test02 {
public static void main(String[] args) {
Father f = new Father();
HashMap<String, String> hashMap = new HashMap<String, String>();
f.doSomething(hashMap);
System.out.println("里氏替換----------------父類存在的地方子類應該也可以存在-----------");
Son s = new Son();
s.doSomething(hashMap);
}
}
/**
* 定義一個父類,實現將map集合轉換成Collection集合
* @author admin
*
*/
class Father{
public Collection doSomething(HashMap<String,String> map){
System.out.println("父類方法被執行...");
return map.values();
}
}
/**
* 子類重載父類的方法
* @author admin
*
*/
class Son extends Father{
/**
* 注意此處是重載,返回值類型,方法名相同,傳入參數不同。
* 保證傳入的參數類型的范圍大于父類。
*/
public Collection doSomething(Map<String, String> map) {
System.out.println("子類方法被執行...");
return map.values();
}
}
~~~
輸出結果:
~~~
父類方法被執行...
里氏替換----------------父類存在的地方子類應該也可以存在-----------
父類方法被執行...
~~~
方法二:
~~~
package hpu.lzl.lsp;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
public class Test02 {
public static void main(String[] args) {
Father f = new Father();
HashMap<String, String> hashMap = new HashMap<String, String>();
f.doSomething(hashMap);
System.out.println("里氏替換----------------父類存在的地方子類應該也可以存在-----------");
Son s = new Son();
s.doSomething(hashMap);
}
}
/**
* 定義一個父類,實現將map集合轉換成Collection集合
* @author admin
*
*/
class Father{
public Collection doSomething(Map<String,String> map){
System.out.println(".......父類方法被執行...");
return map.values();
}
}
/**
* 子類重載父類的方法
* @author admin
*
*/
class Son extends Father{
/**
* 注意此處是重載,返回值類型,方法名相同,傳入參數不同。
*/
public Collection doSomething(HashMap<String, String> map) {
System.out.println("子類方法被執行...");
return map.values();
}
}
~~~
輸出結果:
~~~
.......父類方法被執行...
里氏替換----------------父類存在的地方子類應該也可以存在-----------
子類方法被執行...
~~~
## 覆寫或實現父類的方法時輸出結果可以被縮小
分兩類解釋:一,子類覆寫父類的方法時,要求子類與父類的方法名相同,輸入的參數相同,返回值值范圍小于或等于父類的方法。二,子類重載父類的方法時,要求方法的輸入參數類型或數量不同,在里氏替換原則要求下,子類的輸入參數要大于或等于子類的輸入參數。
我的理解:里氏替換原則,就是在繼承的概念上有定義了一些開發時需要的規范。例如子類繼承父類時,需要擁有父類的方法(這個意思是,在給對象分類時,一定要抽取相同的屬性的方法。例如玩具槍雖然屬性與其他槍相同,但是不能殺死敵人,因而不能繼承AbstractGun類。)。嚴格按照這個規范來進行開發,為后期版本升級,增添子類時都可以很好的維護。
- 前言
- 6大設計原則(一)---單一職責原則
- 6大設計原則(二)---里氏替換原則
- 6大設計原則(三)---依賴倒置原則
- 6大設計模式(四)----接口隔離原則
- 6大設計原則(五)---迪米特法則
- 6大設計原則(六)---開閉原則。
- 設計模式(一)---單例模式
- 設計模式(二)---工廠方法模式
- 設計模式(三)---抽象工廠模式
- 設計模式(四)---模板方法模式
- 設計模式(五)---建造者模式
- 設計模式(六)---代理模式
- 設計模式(七)---原型模式
- 設計模式(八)---中介者模式
- 設計模式(九)---命令模式
- 設計模式(十)---責任鏈模式
- 設計模式(十一)---裝飾模式
- 設計模式(十二)---策略模式
- 設計模式(十三)---適配器模式
- 設計模式(十四)---迭代器模式
- 設計模式(十五)---組合模式
- 設計模式(十六)---觀察者模式
- 設計模式(十七)---門面模式
- 設計模式(十八)---備忘錄模式
- 設計模式(十八)---訪問者模式
- 設計模式(二十)---狀態模式
- 設計模式(二十二)---享元模式
- 設計模式(二十三)---橋梁模式