@Tyhj
2018-07-15T13:33:07.000000Z
字数 2600
阅读 1364
设计模式
原文链接:https://www.zybuluo.com/Tyhj/note/1198744
在使用基类的的地方可以任意使用其子类,能保证子类完美替换基类
通俗来讲,只要父类能出现的地方子类就能出现。反之,父类则未必能胜任
增强程序的健壮性,即使增加了子类,原有的子类还可以继续运行
对于基类中定义的所有子程序,用在它的任何一个派生类中时的含义都应该是相同的。
这样继承才不会增加复杂度,基类才能真正被复用,而派生类也能够在基类的基础上增加新的行为。
如果我们必须要不断地思考不同派生类的实现在语义上的差异,继承就只会增加复杂度了
“正方形不是长方形”是一个理解里氏代换原则的最经典的例子。在数学领域里,正方形毫无疑问是长方形,它是一个长宽相等的长方形。我们开发的一个与几何图形相关的软件系统中,让正方形继承自长方形是顺利成章的事情。
定义一个长方形类:
public class Rectangle {
private int width;
private int height;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
定义一个正方形类,继承长方形:
public class Square extends Rectangle {
@Override
public void setWidth(int width) {
super.setWidth(width);
super.setHeight(width);
}
@Override
public void setHeight(int height) {
super.setHeight(height);
super.setWidth(height);
}
}
类LSPTest是我们的软件系统中的一个组件,它有一个resize方法要用到基类Rectangle,resize方法的功能是模拟长方形宽度逐步增长的效果:
public class LSPTest {
/**
* 让长方形的宽增加到比长大
*
* @param objRect
*/
public void resize(Rectangle objRect) {
while (objRect.getWidth() <= objRect.getHeight()) {
objRect.setWidth(objRect.getWidth() + 1);
}
}
}
我们运行一下这段代码就会发现,假如我们把一个普通长方形作为参数传入resize方法,就会看到长方形宽度逐渐增长的效果,当宽度大于长度,代码就会停止,这种行为的结果符合我们的预期;假如我们再把一个正方形作为参数传入resize方法后,就会看到正方形的宽度和长度都在不断增长,代码会一直运行下去,直至系统产生溢出错误。所以,普通的长方形是适合这段代码的,正方形不适合。
我们得出结论:在resize方法中,Rectangle类型的参数是不能被Square类型的参数所代替,如果进行了替换就得不到预期结果。因此,Square类和Rectangle类之间的继承关系违反了里氏代换原则,它们之间的继承关系不成立,正方形不是长方形。
public void resize(Rectangle objRect) {
if(objRect instanceof Square){
return;
}
if(objRect instanceof Rectangle){
while (objRect.getWidth() <= objRect.getHeight()) {
objRect.setWidth(objRect.getWidth() + 1);
}
}
}
效果好像不错,干嘛讲究那么多呢,实现需求是第一位的,这种写法看起来很很直观的,有利于维护。其实这是违背里氏代换原则的,结果就是可维护性和可扩展性会变差。
可以增加一个抽象类Quadrangle,定义四边形的公共方法,Square和Rectangle都从Quadrangle继承这些方法,同时可以添加自己特有的方法(对于resize方法,正方形不继承长方形,就不存在问题了)。
import java.util.HashMap;
public class Father {
public void func(HashMap m){
System.out.println("执行父类...");
}
}
import java.util.Map;
public class Son extends Father{
public void func(Map m){//方法的形参比父类的更宽松
System.out.println("执行子类...");
}
}
import java.util.HashMap;
public class Client{
public static void main(String[] args) {
Father f = new Son();//引用基类的地方能透明地使用其子类的对象。
HashMap h = new HashMap();
f.func(h);
}
}
import java.util.Map;
public abstract class Father {
public abstract Map func();
}
import java.util.HashMap;
public class Son extends Father{
@Override
public HashMap func(){//方法的返回值比父类的更严格
HashMap h = new HashMap();
h.put("h", "执行子类...");
return h;
}
}
public class Client{
public static void main(String[] args) {
Father f = new Son();//引用基类的地方能透明地使用其子类的对象。
System.out.println(f.func());
}
}