1. 程式人生 > 其它 >00_6面向物件設計原則-里氏替換原則

00_6面向物件設計原則-里氏替換原則

定義:里氏替換原則指如果對每一個型別為T1和物件O1,都有型別T2的物件O2,使得以T1定義的所有程式P在所有物件O1都替換成O2時,程式P的行為沒有發生變化,那麼型別T2是型別T1的子型別。 里氏替換原則通俗來講就是:子類可以擴充套件父類的功能,但不能改變父類原有的功能。也就是說:子類繼承父類時,除新增新的方法完成新增功能外,儘量不要重寫父類的方法。 根據上述理解,對里氏替換原則的定義可以總結如下:
  1. 子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法
  2. 子類中可以增加自己特有的方法
  3. 當子類的方法過載父類的方法時,方法的前置條件(即方法的輸入引數)要比父類的方法更寬鬆
  4. 當子類的方法實現父類的方法時(重寫/過載或實現抽象方法),方法的後置條件(即方法的的輸出/返回值)要比父類的方法更嚴格或相等
下面我們來描述一個經典的業務場景,用正方形、巨型和四邊形的關係說明裡氏替換原則,我們都知道正方形是一個特殊的長方形,那麼可以建立一個長方形的父類Rectangle類,程式碼如下:
package org.test.design.principle.lsp;

public class Rectangle {
    
private long height; private long width; public long getHeight() { return height; } public void setHeight(long height) { this.height = height; } public long getWidth() { return width; } public void setWidth(long width) { this.width = width; } }
Square.java
package org.test.design.principle.lsp;

public class Square extends Rectangle {

    private long length;

    public long getLength() {
        return length;
    }

    public void setLength(long length) {
        this.length = length;
    }

    @Override
    public long getHeight() {
        
return getLength(); } @Override public void setHeight(long height) { setLength(height); } @Override public long getWidth(){ return getLength(); } @Override public void setWidth(long width) { setLength(width); } public static void resize(Rectangle rectangle) { while (rectangle.getWidth() >= rectangle.getHeight()) { rectangle.setHeight(rectangle.getHeight() + 1); System.out.println("Width:" + rectangle.getWidth() + ",Height:" + rectangle.getHeight()); } System.out.println("Resize End,Width:" + rectangle.getWidth() + ",Height:" + rectangle.getHeight()); } }

測試:

public static void main(String[] args) {
    Rectangle rectangle = new Rectangle();
    rectangle.setWidth(20);
    rectangle.setHeight(10);
    resize(rectangle);
}
輸出: Width:20,Height:11 Width:20,Height:12 Width:20,Height:13 Width:20,Height:14 Width:20,Height:15 Width:20,Height:16 Width:20,Height:17 Width:20,Height:18 Width:20,Height:19 Width:20,Height:20 Width:20,Height:21 Resize End,Width:20,Height:21 看到結果可知,高比寬大,這在長方形中是一種非常正常的情況,在來看下面的程式碼,把長方形替換成它的子類正方形,修改客戶端測試程式碼:
public static void main(String[] args) {
        Square square = new Square();
        square.setLength(10);
        resize(square);
}
此時,執行出現死迴圈,違背了里氏替換原則,在將父類替換為子類後,程式執行結果沒有達到預期。因此,程式碼設計存在一定的風險的。里氏替換原則只存在於父類與子類之間,約束繼承氾濫。再來建立一個基於長方形的共同的抽象--四邊形介面,程式碼如下:
package org.test.design.principle.lsp;

public interface QuardRangle {
    long getWidth();
    long getHeight();
}
在來看一下長方形類的V2版本:
package org.test.design.principle.lsp;

public class RectangleV2 implements QuardRangle{
    private long height;
    private long width;

    public void setHeight(long height) {
        this.height = height;
    }

    public void setWidth(long width) {
        this.width = width;
    }

    @Override
    public long getWidth() {
        return width;
    }

    @Override
    public long getHeight() {
        return height;
    }
}
在看看正方形類的V2版本:
package org.test.design.principle.lsp;

/**
 * TODO
 *
 * @author Administrator
 * @version 1.0
 * @date 2021/11/17 20:55
 */
public class SquareV2 implements QuardRangle {
    private long length;

    public long getLength() {
        return length;
    }

    public void setLength(long length) {
        this.length = length;
    }

    @Override
    public long getWidth() {
        return length;
    }

    @Override
    public long getHeight() {
        return length;
    }
}
測試:
 public static void resize(RectangleV2 square) {
        while (square.getWidth() >= square.getHeight()) {
            square.setHeight(square.getHeight() + 1);
            System.out.println("Width:" + square.getWidth() + ",Height:" + square.getHeight());
        }
        System.out.println("Resize End,Width:" + square.getWidth() + ",Height:" + square.getHeight());
    }

    public static void main(String[] args) {
        RectangleV2 rectangle = new RectangleV2();
        rectangle.setWidth(20);
        rectangle.setHeight(10);
        resize(rectangle);
    }
我們在來看一下,如果把resize()方法的引數換成形QuardRangleV2類,方法內部就會報錯。因為正方形已經沒有了setWidth()和setHeigth()方法,所以,為了約束繼承氾濫,resize()方法的引數只能用長方形RectangleV2類。 此篇完。