Liskov substitution(里氏替换)是oop五原则(SOLID)之一,wikipedia上的介绍如下
Substitutability is a principle in object-oriented programming. It states that, in a computer program, if S is a subtype of T, then objects of type T may be replaced with objects of type S (i.e., objects of type S may be substituted for objects of type T) without altering any of the desirable properties of that program (correctness, task performed, etc.).
其中有提到一个比较经典的违反此原则的案例,那就是square和rectangle,先看代码:
public class Rectangle { private double width; private double height; public void setWidth(double width) { this.width = width; } public void setHeight(double height) { this.height = height; } public double getHeight() { return this.height; } public double getWidth() { return this.width; } public double getPerimeter() { return 2*width + 2*height; } public double getArea() { return width * height; } } public class Square extends Rectangle { public void setWidth(double width) { this.width = width; this.height = width; } public void setHeight(double height) { this.height = height; this.width = height; } }
按照自然规则,square是特殊的rectangle(width==height),但是在用两个class来表述时是不符合里氏替换规则的,因为square的setter不满足里氏替换规则里的Postconditions cannot be weakened in a subtype。
那么到底怎样设计才行呢?
简单的说一下我的观点:
1、OOP编程中里氏替换原则只是其一,用以强调设计具有继承关系的一系列类时应尽量满足的基本原则;
2、OOP提供了一种封装数据及其操作的编程思想,但并不是现实世界中的所有事物都能被model成对象的,否则也不会出现类似AOP,面向函数的,面向过程的编程思想;
3、在无法很好地对现实世界的事物建模时,组合优先于继承是一个更实用的OOP基本原则;
4、在你所举的例子中,正方形更多的是描述一种长宽相等的长方形的特例。这个长宽相等更多的是一种对象内部属性的约束关系,而不应作为基本属性。否则下次有个长宽需要固定2:1的特殊长方形呢,那到时又该如何处理?