管理 编码最佳实践——Liskov替换原则

 Liskov替换原则(Liskov Substitution Principle)是一组用于创建继承层次结构的指导原则。按照Liskov替换原则创建的继承层次结构中,客户端代码能够放心的使用它的任意类或子类而不担心所期望的行为。

Liskov替换原则定义

如果S是T的子类型,那么所有的T类型的对象都可以在不破坏程序的情况下被S类型的对象替换。

  • 基类型:客户端引用的类型(T)。子类型可以重写(或部分定制)客户端所调用的基类的任意方法。
  • 子类型:继承自基类型(T)的一组类(S)中的任意一个。客户端不应该,也不需要知道它们实际调用哪个具体的子类型。无论使用的是哪个子类型实例,客户端代码所表现的行为都是一样的。

Liskov替换原则的规则

要应用Liskov替换原则就必须遵守两类规则:

1.契约规则(与类的期望有关)

  • 子类型不能加强前置条件
  • 子类型不能削弱后置条件
  • 子类型必须保持超类型中的数据不变式

2.变体规则(与代码中能被替换的类型有关)

  • 子类型的方法参数必须是支持逆变的
  • 子类型的返回类型必须是支持协变的
  • 子类型不能引发不属于已有异常层次结构中的新异常

契约

我们经常会说,要面向接口编程或面向契约编程。然后,除了表面上的方法签名,接口所表达的只是一个不够严谨的契约概念

作为方法编写者,要确保方法名称能反应出它的真实目的,同时参数名称要尽可能使描述性的。

public decimal CalculateShippingCost(int count,decimal price) {     return count * price; }

然而,方法签名并没有包含方法的契约信息。比如price参数是decimal类型的,这就表明任何decimal类型的值都是有限的。但是price参数的意义是价格,显然价格不能是负数。为了做到这一点,要在方法内部实现一个前置条件。

前置条件

前置条件(precondition)是一个能保障方法稳定无错运行的先决条件。所有方法在被调用钱都要求某些前置条件为真。

引发异常是一种强制履行契约的高效方式:

public class ShippingStrategy {     public decimal CalculateShippingCost(int count,decimal price)     {         if(price <= Decimal.Zero)         {             throw new Exception();         }         return count * price;     } }

更好的方式是提供详尽的前置条件校验失败原因,便于客户端快速排查问题。此处抛出参数超出了有效范围,并且明确指出了是哪一个参数。

public class ShippingStrategy {     public decimal CalculateShippingCost(int count, decimal price)     {         if (price <= Decimal.Zero)         {             throw new ArgumentOutOfRangeException("price", "price must be positive and non-zero");         }         return count * price;     } }

有了这些前置条件,客户端代码就必须在调用方法钱确保它们传递的参数值要处于有效范围内。当然,所有在前置条件中检查的状态必须是公开可访问的。私有状态不应该是前置条件检查的目标,只有方法参数和类的公共属性才应该有前置条件。

后置条件

后置条件会在方法退出时检测一个对象是否处于一个无效的状态。只要方法内改动了状态,就用可能因为方法逻辑错误导致状态无效。

关键字:
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信