1. 程式人生 > >裡式替換原則——面向物件程式設計原則

裡式替換原則——面向物件程式設計原則

目錄

  • 定義
  • 意義
  • 做法
  • 實踐
    • uml圖
    • 程式碼部分

github倉庫

定義

Liskov於1987年提出了一個關於繼承的原則“Inheritance should ensure that any property proved about supertype objects also holds for subtype objects.”—— 繼承必須確保超類所擁有的性質在子類中仍然成立.。通俗的來講就是子類可以擴充套件父類的功能,但是不能改變父類原有的功能。

該原則稱為Liskov Substitution Principle——里氏替換原則。

里氏替換原則主要闡述了有關繼承的一些原則,也就是什麼時候應該使用繼承,什麼時候不應該使用繼承,以及其中蘊含的原理。里氏替換原是繼承複用的基礎,它反映了基類與子類之間的關係,是對開閉原則的補充,是對實現抽象化的具體步驟的規範。

意義

  1. 防止重寫父類方法,出現父類複用性差的情況。

  2. 程式執行正確性的保證,即類的擴充套件不會給系統帶來新的錯誤,降低了出錯的可能性。因為子類重寫了父類方法,在使用多型特性時,程式可能會出現不可預知的錯誤。

做法

  • 子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法。

  • 子類中可以增加自己特有的方法。

  • 當子類的方法過載父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入引數更寬鬆。

  • 當子類的方法實現父類的抽象方法時,方法的後置條件(即方法的返回值)要比父類更嚴格。

實踐

在英雄聯盟中,每個英雄到了6級都會擁有大招,大招的效果隨著英雄等級提升而提升,盲僧的大招有傷害,傑斯的大招只是切換形態,沒有傷害。我們在傑斯類中覆蓋了父類的方法,導致呼叫getrDPS()方法出現了意料之外的錯誤。

(後話系列)對於這種情況我們應該將Hero再細分為兩種子類,一種是大招有傷害型別,一種是無傷害型別,LeeSin和Jess分別繼承他們,將Hero做成頂級類,只擁有管理等級的功能。

uml圖

程式碼部分

英雄基類

/**
 * 英雄基類
 */
public class Hero {
    /** R 技能傷害 */
    private double rDPS;

    /** 英雄等級 */
    private int clas;

    public void setClas(int clas){
        this.clas = clas;
    }

    /**
     * R技能伴隨等級的變化
     */
    public void getrDPS(){
        rDPS = 2100 / clas * 3.97;
        System.out.println("R技能的傷害為" + rDPS);
    }
}

盲僧

/**
 * 盲僧
 */
public class LeeSin extends Hero{}

傑斯

/**
 * 傑斯
 */
public class Jess extends Hero{
    @Override
    public void setClas(int clas) {
        System.out.println("雖然我到6了,但咱大招麼得傷害呀,就不用傳值等級了,反正也沒用");
    }
}

測試類

public class Main {
    public static void main(String[] args) {
        Hero leeSin = new LeeSin();
        leeSin.setClas(6);
        leeSin.getrDPS();

        Hero jess = new Jess();
        jess.setClas(6);
        jess.getrDPS();
    }
}

因為傑斯大招沒傷害,重寫了父類的setClas(int clas)方法沒有為clas賦值,故而在計算R技能傷害時發生了除零異常。

R技能的傷害為1389.5
雖然我到6了,但咱大招麼得傷害呀,就不用傳值等級了,反正也沒用
Exception in thread "main" java.lang.ArithmeticException: / by zero