《设计模式》杂记之里氏替换原则(续)

3,覆盖或实现父类方法时输入参数可以被放大
我们可能都知道方法中的输入参数称为前置条件,在里氏替换原则中也有一个契约,就是父类或接口。契约制定了,也就同时制定了前置条件和后置条件,前置条件就是你要让我执行,就必须满足我的条件;后置条件就是我执行完了需要反馈,标准是什么(大家可能对作者的这句话不很理解,其实我和大家刚看的时候都一样不理解啊!)。
那么我们还是通过作者的例子来理解理解吧!呵呵~~(这里我直接用作者的java版代码啦!)
Father类源代码
public class Father {
public Collection doSomething(HashMap map){
System.out.println("父类被执行...");
return map.values();
}
}
子类源代码
public class Son extends Father {
//放大输入参数类型
public Collection doSomething(Map map){
System.out.println("子类被执行...");
return map.values();
}
}
通过子类,我们可以发现子类重载了父类方法。那么在场景类中调用代码如下:
public class Client {
public static void invoker(){
//父类存在的地方,子类就应该能够存在
//Father f = new Father();
Son f =new Son();
HashMap map = new HashMap();
f.doSomething(map);
}
public static void main(String[] args) {
invoker();
}
}
更具里氏替换原则,父类出现的地方子类就可以出现。
public class Client {
public static void invoker(){
//有父类的地方就有子类
//Father f= new Father();
Son f =new Son();
HashMap map = new HashMap();
f.doSomething(map);
}
public static void main(String[] args) {
invoker();
}
}
这两个的运行结果都是一样,通过代码我们了解到父类方法输入参数是HashMap类型,子类的输入参数是Map类型,可以理解成子类的输入参数类型的范围扩大了,子类代替父类传递到调用者中,子类的方法永远都不会被执行。
如果我们想让子类的方法运行,就必须覆写父类方法
那么我们现在反过来想想,如果Father类的输入参数类型宽于子类的输入参数类型。会出现父类存在的地方,子类未必可以存在,因为一旦我们把子类作为参数传入,使用者就很可能进入子类的方法范畴。
所以子类中方法方法的前置条件必须与超类中被覆写的方法的前置条件相同或者更宽松。
4,覆盖或实现父类方法输出结果可以被缩小
父类的一个方法的返回值是一个类型T,子类的相同方法(重载或覆写)的返回值为S,在里氏替换原则中要么S和T是同一个类型,要么S是T的子类。
如果是覆写,父类和子类的同名方法的输入参数是相同的,两个方法的范围值S小于等于T。
如果是重载,则要求方法的输入参数类型或数量不相同,在里氏替换原则要求下,就是子类的输入参数宽于或等于父类的输入参数。
引用作者的一段话:我们采用里氏替换原则目的是增强程序的健壮性,版本升级时也可以保持非常好的兼容性,即使增加子类,原有的子类还可以继续运行。

相关文章

适配器模式将一个类的接口转换成客户期望的另一个接口,使得原本接口不兼容的类可以相互合作。
策略模式定义了一系列算法族,并封装在类中,它们之间可以互相替换,此模式让算法的变化独立于使用算法...
设计模式讲的是如何编写可扩展、可维护、可读的高质量代码,它是针对软件开发中经常遇到的一些设计问题...
模板方法模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中,使得子类可以在不改变算法结...
迭代器模式提供了一种方法,用于遍历集合对象中的元素,而又不暴露其内部的细节。
外观模式又叫门面模式,它提供了一个统一的(高层)接口,用来访问子系统中的一群接口,使得子系统更容...