携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情
简介
里氏替换原则说起来比较抽象,中文翻译过来是: 某个对象实例的子类实例应当可以在不影响程序正确性的基础上替换它们。
我的理解是将某个功能的基类对象换成它某个子类的对象,程序将任然能够正常运行;反过来如果将某子类的对象换成其基类的对象,则程序无法正常运行。即子类可以新增父类没有的方法,但是不能重写父类的方法。所以我们在开发中一般定义变量时会定义其类型为基类,真正到使用此对象时才去实例化对应的子类指向此基类。
说到这里,里氏替换原则实际上是非常遵循开闭原则
应用
相信大家都知道MybatisPlus这个强大的开源库,很多人可能都经常使用这个框架,我们就从这个库从摘取一个例子来说明里氏替换原则的必要性。
MybatisPlus为我们提供了一个ServiceImpl,顾名思义这个类帮我们封装了很多常用通用的数据库操作方法,只需要在各个UserServiceImpl,RoleServiceImpl去继承这个ServiceImpl即可直接调用一系列的增删改查方法,而不用在各个业务Service、Mapper中去定义,减少了很多重复代码,极大提高了数据库操作的便捷性。比如获取某个表的第一条数据:
@Override
public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
if (throwEx) {
return baseMapper.selectOne(queryWrapper);
}
return SqlHelper.getObject(log, baseMapper.selectList(queryWrapper));
}
如果我们在UserServiceImpl继承了ServiceImpl,即可使用this.getOne,传入条件即可查询想要的数据。到目前为止,一切都很美好。但是。。。
如果这个时候有位不熟悉的同学不知道咱们MybatisPlus已经提供了这个方法,觉得每次都要传入一个状态字段status比较麻烦,干脆给封装进去,以后使用都不用加这个条件了,岂不完美,于是自豪地重写了这个方法:
@Override
public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
queryWrapper.eq("status", 1);
return baseMapper.selectOne(queryWrapper);
}
殊不知之前的某个功能偏偏要把所有数据都查出来,不过滤status字段,经过这一翻重写,以前的功能反而出现问题了。
总结
1、当子类的方法重载父类的方法时,方法的入参要比父类方法的输入参数更宽松
2、当子类的方法实现父类的方法时(重写/重载或实现抽象方法),方法的后置条件输出和可能抛出的异常要比父类更严格或与父类一样