Liskov替换原则(LSP)
Liskov替換原則(The Liskov Substitution Principle)
子類型(subtype)必須能夠替換掉它們的基類型(base type)。一個違反LSP的簡單例子
public class AntiLspDemo {public void drawShape(final Shape shape) {if (shape.getItsType() == ShapeType.Square) {final Square square = (Square) shape;square.draw();} else if (shape.getItsType() == ShapeType.Circel) {final Circle circle = (Circle) shape;circle.draw();}} }enum ShapeType {/*** 正方形.*/Square,/*** 圓形.*/Circel }class Point {private double x;private double y;// get/set... }class Shape {private ShapeType itsType;/*** 獲取itsType.* @return the itsType*/public ShapeType getItsType() {return itsType;}/*** 設(shè)置itsType.* @param newItsType the itsType to set*/public void setItsType(ShapeType newItsType) {itsType = newItsType;}}class Circle extends Shape {private Point itsCenter;private double itsRadius;public Circle() {this.setItsType(ShapeType.Circel);}public void draw() {System.out.println("繪制Circle...");} }class Square extends Shape {private Point itsTopLeft;private double itsSide;public Square() {this.setItsType(ShapeType.Square);}public void draw() {System.out.println("繪制Square...");} }正方形和矩形,更微妙的違規(guī)
? ? ? ? 我們經(jīng)常說繼承是IS-A(“是一個”)關(guān)系。如果一個新類型的對象被認(rèn)為和一個已有類的對象之間滿足IS-A關(guān)系,那么這個新對象的類應(yīng)該從這個已用對象的類派生。
? ? ? ? 一個正方形是一個矩形,所以Square類就應(yīng)該派生在Rectangle類。不過,這將帶來一些微妙但極為值得重視的問題。
public class Rectangle {/*** 左上角坐標(biāo).*/private Point topLeft;/*** 寬度.*/private double width;/*** 高度.*/private double height;/*** 獲取topLeft.* @return the topLeft*/public Point getTopLeft() {return topLeft;}/*** 設(shè)置topLeft.* @param newTopLeft the topLeft to set*/public void setTopLeft(Point newTopLeft) {topLeft = newTopLeft;}/*** 獲取width.* @return the width*/public double getWidth() {return width;}/*** 設(shè)置width.* @param newWidth the width to set*/public void setWidth(double newWidth) {width = newWidth;}/*** 獲取height.* @return the height*/public double getHeight() {return height;}/*** 設(shè)置height.* @param newHeight the height to set*/public void setHeight(double newHeight) {height = newHeight;}}public class Square extends Rectangle {public void setWidth(double width) {super.setWidth(width);super.setHeight(width);}public void setHeight(double height) {super.setWidth(height);super.setHeight(height);} }真正的問題
? ? ? ? Square類沒有違反正方形的不變性,但是Square派生自Rectangle,Square類違反了Rectangle類的不變性。
public void g(Rectangle r) {r.setWidth(5);r.setHeight(4);assert(r.area() == 20) }有效性并非本質(zhì)屬性
? ? ? ? 一個模型,如果孤立地看,并不具有真正意義上的有效性。模型的有效性只能通過它的Client程序來體現(xiàn)。這又是一個實踐TDD的好理由。
IS-A是關(guān)于行為的
? ? ? ? 對于那些不是g的調(diào)用者而言,正方形可以是長方形,但是從g的角度,Square對象絕對不是Rectangle對象。Square對象的行為方式和函數(shù)g所期望的Rectangle對象的行為方式不相容。從行為方式的角度來看,Square不是Rectangle,對象的行為方式才是軟件真正所關(guān)注的。
基于契約設(shè)計
? ? ? ? 基于契約設(shè)計(Design By Contract),類的編寫者顯示地規(guī)定針對該類的契約。契約是通過為每個方法聲明的前置條件(preconditions)和后置條件(postconditions)來指定的。要執(zhí)行一個方法,前置條件必須為真。執(zhí)行完畢后,保證后置條件為真。?
Rectangle.setWidth(double w)的后置條件: assert((width == w) && (height == old.height));? ? ? ? 派生類的前置條件和后置條件規(guī)則是:
在重新聲明派生類中的例程(routine)時,
只能使用相等或更弱的前置條件,只能使用相等或更強(qiáng)的后置條件。
總結(jié)
以上是生活随笔為你收集整理的Liskov替换原则(LSP)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 集合对象-“块数据”操作--其实是同一对
- 下一篇: 几种限流器(RateLimiter)原理