里氏代换原则
基本介绍
里氏代换原则(Liskov Substitution Principle, LSP),意为所有引用父类的地方必须能透明地使用其子类的对象。
里氏代换原则告诉我们,在软件中将一个基类对象替换成它的子类对象,程序将不会产生任何错误和异常,反过来则不成立,如果一个软件实体使用的是一个子类对象的话,那么它不一定能够使用基类对象。例如:我喜欢动物,那我一定喜欢狗,因为狗是动物的子类;但是我喜欢狗,不能据此断定我喜欢动物,因为我并不喜欢老鼠,虽然它也是动物。
例如有两个类,一个类为BaseClass,另一个是SubClass类,并且SubClass类是BaseClass类的子类,那么一个方法如果可以接受一个BaseClass类型的基类对象base的话,如:method1(base),那么它必然可以接受一个BaseClass类型的子类对象sub,method1(sub)能够正常运行。反过来的代换不成立,如一个方法method2接受BaseClass类型的子类对象sub为参数:method2(sub),那么一般而言不可以有method2(base),除非是重载方法。里氏代换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类对象。
注意事项
-
子类的所有方法必须在父类中声明,或子类必须实现父类中声明的所有方法。根据里氏代换原则,为了保证系统的扩展性,在程序中通常使用父类来进行定义,如果一个方法只存在子类中,在父类中不提供相应的声明,则无法在以父类定义的对象中使用该方法。
-
我们在运用里氏代换原则时,尽量把父类设计为抽象类或者接口,让子类继承父类或实现父接口,并实现在父类中声明的方法,运行时,子类实例替换父类实例,我们可以很方便地扩展系统的功能,同时无须修改原有子类的代码,增加新的功能可以通过增加一个新的子类来实现。里氏代换原则是开闭原则的具体实现手段之一。
代码示例
原始代码:
public class MailUtil {
public static void send(VipUser vipUser){
}
public static void send(CommonUser commonUser){
}
}
public class VipUser {
private String userName;
private String email;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
public class CommonUser {
private String userName;
private String email;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
从上述代码可以看到MailUtil
为了支持为不同类型的用户发送邮件,因此有两个独立的send
方法,分别为VipUser
和CommonUser
发送邮件,发送邮件的过程其实都是一样的,如果后续需要增加其它用户类型的,此种做法可扩展性就不太好了,并且重复代码太多,下面我们根据里氏代换原则来进行重构:
public class MailUtil {
public static void send(BaseUser user){
}
}
public class BaseUser {
private String userName;
private String email;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
public class CommonUser extends BaseUser{
}
public class VipUser extends BaseUser{
}
我们新增了一个用户基类BaseUser
,VipUser
和CommonUser
分别继承BaseUser
,根据里氏代换原则,能够接受基类对象的地方必然能够接受子类对象,因此将MailUtil
的send
方法参数类型变更为BaseUser
,即可同时为不同类型的用户发送邮件,如果需要增加其它的用户类型,也只需要继承BaseUser
即可。