Liskov替换原则

Liskov替换原则(Liskov Substitution Principle,LSP):
     是一组用于创建继承层次结构的指导原则,按照Liskov替换原则创建的继承层次结构中,客户端代码能够放心的使用他的任意类或子类而不担心影响所期望的行为。
      (如果S是T的子类型,那么所有T类型的对象都可以在不破坏程序的情况下被S类型替换)


契约:
      前置条件(precondition):一个能保证方法稳定无错运行的先决条件。所有方法在被调用前要求某些前置条件为真。(添加防卫语句判断参数是否有效)。所有的前置条件检查的状
态必须是公开访问的。(客户端无法保证私有状态有效。)
      后置条件(postcondition):在方法退出是检查一个对象是否处于无效状态。与实现前置条件一样,可以使用防卫语句实现后置条件。
       数据不变式(data invariant):是在一个对象生命周期内始终都保持为真的一个谓词。该谓词条件在从构造后一直到超出其作用范围前这段时间内都为真。(即将防卫语句添加到构造
函数中或者属性设置器中,从客户端传入的成员变量永远有效)。
       封装与契约:可以将传入的参数封装为一个类,在类中对变量进行契约设置。
       Lisov契约规则:1.子类型不能加强前置条件;2.子类型不能削弱后置条件;3.子类型必须保持超类型中的数据不变式。Liskov替换原则明确规定了继承时一些变更是被禁止的,因为
他们会导致原有的使用超类实例的已有客户端代码在切换子类时必须要做更改。
               1.前置条件不能被加强:当子类重写包含前置条件的超类方法时,不应该加强现有的前置条件。这样做很可能会影响到那些已经假设超类的所有方法定义了最严格的前置条件
契约的客户端代码。(如果子类增强了前置条件,创建超类引用时,原有的超类契约变得无效化。客户端需要判断类类型,这样增加了类与客户端的耦合性)
               2.后置条件不能被削弱:已有的客户端代码在原有的超类切换至新的子类时可能会出错。(如果严格遵守Liskov替换原则,你所创建的所有子类都能够被所有已有的客户端代
码使用且不会出现意料之外的错误)例:如果超类后置条件设置函数范围值大于0,客户端按照遵循这个契约。但是当某个子类弱化后置条件,使得返回值可以等于0,那么前面客户端遵循的
契约将会一直报错。需要修改客户端的代码。
               3.数据不变式必须被保持:创建新的子类时,必须继续遵守基类中的所有数据不变式。因为子类有很多机会来改变基类中的私有数据。
       代码契约:引用system.Diagnostics.Conracts命名空间后,使用Contract静态类中提供的实现契约的主要功能。(注:如果使用这种代码契约方式,代码中的许多位置都有这个静态
类,如果想要删除,工作量有些大。所以要是想使用,则全部位置都使用)


协变和逆变:
         1、协变(covariance):在使用泛型参数的时候,ICovariant<out T>泛型参数T使用基类类型,但是当传入对象为子类类型时也同样有效,这样的效果是由协变和多态的紧密配合
。(继承是不具备协变的能力)。在设计接口的时候,如果需要使用协变的能力,建议在设计接口的时候添加泛型参数来设置返回类型,因为泛型支持协变。
         2、逆变(contravariance):逆变是一个类似协变的概念。协变只是与方法的返回类型的处理相关,而逆变是与方法参数类型的处理相关。IContravariant<in T>接口定义的方法只接受由泛型参数指定类型的单个参数。
         3、不变性(data invariant):如果泛型参数没有设置in out关键字,那么这个类型是不变体,不支持协变、逆变。(只有在设计泛型时残能将类型定义为可协变的或可逆变的)
Liskov类型系统规则:
         1、子类型的方法参数必须是可逆变的
         2、子类型的返回类型必须是可协变的
         3、不允许引发新的异常(每个接口都应该有一个统一的基础异常类型,他可以将必要的错误信息从异常汇报器传给异常处理器)
       (只有方法参数支持逆变而且返回类型支持协变,才可以编写出遵循Liskov替换原则的代码)
         

猜你喜欢

转载自blog.csdn.net/Sbjhy/article/details/81433179