Liskov替换原则(LSP)

OCP中,继承支持了抽象和多态特性.

  • LSP:子类必须能够替换掉其基类.
    1. 反例:使用if/else判断类型,以便选择针对特定类型的正确行为.
    2. 有效性并非本质属性
      1. 模型的有效性,只能通过它的客户程序来表现.
      2. 在考虑一个特定设计是否合理时,必须要根据该设计的使用者所作出的合理假设来审视它.
      3. 这些合理的假设常常就是单元测试中的assert.
      4. 不要试图做所有的假设,而只是优先预测那些明显的对于LSP原则的违反情况,而在相关的脆弱性臭味出现时,才做其他的预测.
    3. IS-A是关于行为方式的.
      1. 对象的行为方式才是软件所关注的.
      2. 行为方式是可以进行合理假设的,是客户程序所依赖的.
  • DBC:基于契约(Contract)设计.
    1. Class的编写者显示地规定针对该类的契约,Client代码的编写者通过契约获取可以依赖的行为方式.
    2. 契约通过为方法声明precondition和postcondition来指定.
    3. :如果X没有遵守Y的所有约束,那么X就比Y弱.而无视X所遵守的新约束的数目.
    4. 断言重新声明规则:在派生类的重载中,只能使用相等或更弱的precondition来替代原始的,同时只能使用相等或者更前的postcondition来替换原始的.
    5. 在单元测试中指定契约.
  • 提取公共部分的方法替代继承
    1.  
    2. 对应于情况1,接受一个多态行为中的微妙错误和试着修改设计来完全符合LSP,两者谁更加有利是一个需要权衡(接受缺陷放弃完美)的问题.
    3. 总是保证子类可以替代它的基类是一个有效的管理复杂性的方法,一旦放弃了LSP,就必须要单独考虑每个子类.
    4. 提取公共部分,最后在代码不是很多的时候应用.如果类Line已经有很多的客户,那么提取出LinearObject是不容易的.
      1. 如果一组类都支持一个公共的职责,那么它们应该从一个公共的超类继承该职责.
  • 启发式规则和习惯用法
    1. 在派生类中存在退化函数,预示着可能违反了LSP原则.
    2. 去除功能的派生类,完成的功能少于其基类的派生类是不能替换其基类的.
    3. 在派生类中添加了基类不会抛出的异常.基类的使用者是不会期望这些异常的.

结论. LSP原则中的子类型的可替换性(隐式可以依赖的)能够使得基类型的模块在无需修改时可以进行扩展.

子类型:可替换性的.

使用显示或者隐式的契约来定义可替换性.

posted on 2014-05-15 09:08  RobynHYB  阅读(345)  评论(0编辑  收藏  举报

导航