里氏替换原则的定义有:
如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有变化,那么类型T2是类型T1的子类型。
定义很完备,但也不太好理解。说白了,其实就是使用到父类的地方都可以完全的替换成它的某个子类,而整个程序的运行不受任何影响。也就是说,使用父类对象调用某个成员方法,和使用其某个子类对象调用同名的成员方法,在逻辑上和结果上都需要是等价的。
这规定了,继承父类的子类,必须完完全全,不打折扣的支持父类的方法。
子类兼容父类
这条原则,是面向对象中多态的基石。在程序设计时,我们常以父类的引用来接接受某个子类的对象,再以父类的身份调用这些方法,他们可以是已经在父类中实现的,也可以是在父类中声明了原型而在子类中分别实现的。但是在调用的时候,应该看不出通过子类调用还是通过父类调用。
我们看个例子:
abstract class Animal
{
public void die()
{
System.out.println("animal dead~~");
}
public abstract void move();
}
class Bird extends Animal
{
public void move()
{
System.out.println("flying~~");
}
}
class Human extends Animal
{
public void move()
{
System.out.println("running~~");
}
public void hunt(Animal target)
{
target.move();
this.move();
target.die();
}
}
在这个例子中,人可以猎杀一个动物,这个动物是一个父类,将它替换成某个具体的子类 Bird 或者 Human都不会对程序的运行产生任何影响。
为了实现这个要求,通常情况下,在父类方法已经实现的情况下,子类尽可能不要重写父类的方法。
当子类重载父类方法时,参数类型的设计应当将范围扩大。
父类不需要兼容子类
该原则仅规定了子类可以替换父类,而父类确实不必要可以完全兼容子类的。
子类可以在父类的基础上可以添加新的方法和成员,例如在上面的程序中,人类就多出了捕猎的方法,这时用父类对象替换子类就是不可取的,因为父类可能没有子类新增的方法。
不要忘记接口
接口可以看作一种特殊的抽象类,实现接口的类可以看作是接口的子类(尽管java的单继承仅允许存在一个父类)。
我们可以将上面的例子更改一下,抽象出一个捕食者的接口,所有实现这个接口的类都可以进行捕猎:
abstract class Animal
{
public void die()
{
System.out.println("animal dead~~");
}
public abstract void move();
}
interface Hunter
{
public void hunt(Animal target);
}
class Bird extends Animal
{
public void move()
{
System.out.println("flying~~");
}
}
class Shark extends Animal implements Hunter
{
public void move()
{
System.out.println("swimming~~");
}
public void hunt(Animal target)
{
target.move();
this.move();
target.die();
}
}
class Human extends Animal implements Hunter
{
public void move()
{
System.out.println("running~~");
}
public void hunt(Animal target)
{
target.move();
this.move();
target.die();
}
}
...
{
Animal a = new human();
Hunter h = new Shark();
Shark s = new Shark();
h.hunt(a);
s.hunt(a);
}
此时,作为捕食者的鲨鱼拥有捕食方法,将捕食者对象替换为它的实现类鲨鱼类对象时,这个调用在结果上仍然相同
系列文章参考资料:
- 图解Java设计模式 。视频很全也很棒,墙裂推荐。视频就在站内。
- 《设计模式》 设计模式的经典。
- 《大话设计模式》非常有名的小白友好型设计模式著作。
设计模式系列文章:
面向对象 之 不能不知道的类间关系(上)泛化、实现、依赖 C++与Java
面向对象 之 不能不知道的类间关系(下)关联、聚合、组合 C++与Java
JAVA单例模式分析及其实现:饿汉式、懒汉式、双重检查、静态内部类
设计模式 之 迪米特法则
设计模式 之 设计原则(1) 单一职责原则 接口隔离原则 依赖倒转原则