00_6面向物件設計原則-里氏替換原則
阿新 • • 發佈:2021-11-18
定義:里氏替換原則指如果對每一個型別為T1和物件O1,都有型別T2的物件O2,使得以T1定義的所有程式P在所有物件O1都替換成O2時,程式P的行為沒有發生變化,那麼型別T2是型別T1的子型別。
里氏替換原則通俗來講就是:子類可以擴充套件父類的功能,但不能改變父類原有的功能。也就是說:子類繼承父類時,除新增新的方法完成新增功能外,儘量不要重寫父類的方法。
根據上述理解,對里氏替換原則的定義可以總結如下:
- 子類可以實現父類的抽象方法,但不能覆蓋父類的非抽象方法
- 子類中可以增加自己特有的方法
- 當子類的方法過載父類的方法時,方法的前置條件(即方法的輸入引數)要比父類的方法更寬鬆
- 當子類的方法實現父類的方法時(重寫/過載或實現抽象方法),方法的後置條件(即方法的的輸出/返回值)要比父類的方法更嚴格或相等
package org.test.design.principle.lsp; public class Rectangle {Square.javaprivate 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; } }
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類。 此篇完。