里斯科夫替换_里斯科夫换人原则:用词不当?

里斯科夫替换

In an influential keynote address on data abstraction and class hierarchies at the OOPSLA 1987 programming language research conference, Barbara Liskov said the following: “What is wanted here is something like the following substitution property: If for each object o₁ of type S there is an object o₂ of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o₁ is substituted for o₂, then S is a subtype of T.” [1]

在上在OOPSLA 1987编程语言的研究会议数据抽象和类层次结构一个有影响力的主题演讲中,芭芭拉Liskov的下面说:“这到底是怎么想的东西像下面的替代属性:如果类型的每个对象O 1那儿是对象类型为T的o 2 ,这样对于用T定义的所有程序P,当用o 1代替o 2时,P的行为不变,则S是T的子类型。” [1]

This characterisation has since been widely known as the Liskov Substitution Principle (LSP).

此特征此后被广泛称为Liskov替代原理(LSP)。

Unfortunately, though, it has several issues.

不幸的是,它有几个问题。

Firstly, in its original formulation, it is too strong: we rarely want the behavior of a subclass to be identical to that of its superclass; substituting a subclass object for a superclass object is often done with the intent to change the program’s behavior, albeit in a way that maintains the program’s desirable properties.

首先,在其原始表述中,它太强大了:我们很少希望子类的行为与其父类的行为相同; 用子类对象代替超类对象通常是为了改变程序的行为,尽管这样做可以保持程序的期望属性。

Secondly, it makes no mention of specifications, so it invites an incorrect reading where the implementation of type S is compared to the implementation of type T. This is problematic for several reasons, one being that it does not support the common case where T is abstract and has no implementation.

其次,它没有提到的规格 ,所以邀请一个不正确的阅读,其中S型的实施相比,类型的实现 T.这是有几个原因存在问题,其中之一是,它不支持常见的情况,其中T是抽象,没有实现。

Thirdly, and most subtly, in the context of object-oriented imperative programming it is difficult to define precisely what it means to universally or existentially quantify over objects of a given type, or to substitute one object for another. [2]

第三,也是最微妙的是,在面向对象的命令式编程的上下文中,很难准确定义对给定类型的对象进行普遍或存在量化或将一个对象替换为另一个对象的含义。 [2]

Consider a program with an abstract superclass Bag and a concrete subclass Stack. In such a program, we are not substituting a Stack object for a Bag object; indeed, class Bag has no objects of its own. Instead, we are simply using a Stack object as a Bag object.

考虑一个具有抽象超类Bag和具体子类Stack的程序。 在这样的程序中,我们并没有用Stack对象代替Bag对象; 实际上,Bag类没有自己的对象。 相反,我们只是将Stack对象用作Bag对象。

In an interview in 2016, Liskov herself explains that what she presented in her keynote address was an “informal rule”, that Jeannette Wing later proposed that they “try to figure out precisely what this means”, which led to their joint publication on behavioral subtyping, and indeed that “technically, it’s called behavioral subtyping”. During the interview, she does not use substitution terminology to discuss the concepts. [3]

在2016年的一次采访中,利斯科夫本人解释说,她在主题演讲中提出的是“非正式规则”,珍妮特·温后来提出,他们“试图精确地找出这意味着什么”,这导致他们共同发表了关于行为子类型化,实际上是“在技术上,这被称为行为子类型化”。 在面试期间,她没有使用替代术语来讨论概念。 [3]

Image for post
Prof. Barbara Liskov
芭芭拉·里斯科夫(Barbara Liskov)教授

Indeed, in 1994 Liskov herself, together with her co-author Jeannette Wing, gave us the terminology we need to speak correctly about the semantic correctness of subclass relationships in object-oriented programming: behavioral subtyping.

的确,1994年,Liskov自己与她的合著者Jeannette Wing一起给了我们一个术语,我们需要正确地谈论面向对象编程中子类关系的语义正确性:行为子类型化。

Behavioral subtyping is the principle that properties that clients can prove using the specification of an object’s presumed type should hold even though the object is actually a member of a subtype of that type. [4]

行为子类型化是一个原则,即即使对象实际上是该类型的子类型的成员,客户端也可以使用该对象的假定类型的规范来证明其属性。 [4]

For example, consider a type Stack and a type Queue, that both have a put method to add an element and a get method to remove one. Suppose the documentation associated with these types specifies that type Stack’s methods shall behave as expected for stacks (i.e. they shall exhibit LIFO behavior), and that type Queue’s methods shall behave as expected for queues (i.e. they shall exhibit FIFO behavior). Suppose, now, that type Stack were declared as a subclass of type Queue. Most programming language compilers ignore documentation and perform only the checks that are necessary to preserve type safety. Since, for each method of type Queue, type Stack provides a method with a matching name and signature, this check would succeed. However, clients accessing a Stack object through a reference of type Queue would, based on Queue’s documentation, expect FIFO behavior but observe LIFO behavior, invalidating these clients’ correctness proofs and potentially leading to incorrect behavior of the program as a whole.

例如,考虑一个Stack类型和一个Queue类型,它们都有一个put方法添加一个元素和一个get方法删除一个元素。 假设与这些类型相关的文档指定了Stack类型的方法应表现出对堆栈的期望(即,它们应表现出LIFO行为),而Queue类型的方法应表现出对队列的预期(即,它们表现出FIFO行为)。 现在,假设将该类型Stack声明为Queue类型的子类。 大多数编程语言编译器都忽略文档,而仅执行保持类型安全所必需的检查。 由于对于Queue类型的每种方法,Stack类型都提供了具有匹配名称和签名的方法,因此此检查将成功。 但是,基于Queue的文档,通过Queue类型的引用访问Stack对象的客户端将期望FIFO行为,但会观察到LIFO行为,从而使这些客户端的正确性证明无效,并有可能导致整个程序的错误行为。

This example violates behavioral subtyping because type Stack is not a behavioral subtype of type Queue: it is not the case that the behaviors allowed by the specification of Stack are also allowed by the specification of Queue.

此示例违反了行为子类型,因为类型Stack不是Queue类型的行为子类型:并非Queue规范也允许Stack规范允许的行为。

In contrast, a program where both Stack and Queue are subclasses of a type Bag, whose specification for get is merely that it removes some element, does satisfy behavioral subtyping and allows clients to safely reason about correctness based on the presumed types of the objects they interact with. Indeed, any object that satisfies the Stack or Queue specification also satisfies the Bag specification.

相比之下,其中Stack和Queue都是Bag类型的子类的程序,其获取的规范仅仅是删除了某些元素,满足了行为子类型,并允许客户端基于他们所假定的对象类型来安全地推断正确性。与。。。相互作用。 实际上,任何满足Stack或Queue规范的对象也都满足Bag规范。

It is important to stress that whether a type S is a behavioral subtype of a type T depends only on the specification of type T; the implementation of type T, if it has any, is completely irrelevant to this question. Indeed, type T need not even have an implementation; it might be a purely abstract class. As another case in point, type Stack above is a behavioral subtype of type Bag even if type Bag’s implementation exhibits FIFO behavior: what matters is that type Bag’s specification does not specify which element is removed by method get. This also means that behavioral subtyping can be discussed only with respect to a particular (behavioral) specification for each type involved, and that if the types involved have no well-defined behavioral specification, behavioral subtyping cannot be discussed meaningfully.

必须强调的是,类型S是否是类型T的行为子类型,仅取决于类型T的规范 。 T类型的实现 (如果有的话)与这个问题完全无关。 实际上,类型T甚至不需要实现。 它可能是一个纯粹的抽象类。 作为另一种情况,即使类型Bag的实现表现出FIFO行为,上面的Stack类型也是Bag类型的行为子类型:重要的是类型Bag的规范未指定方法get删除了哪个元素。 这也意味着只能针对所涉及的每种类型的特定(行为)规范来讨论行为子类型,并且如果所涉及的类型没有明确定义的行为规范,则不能有意义地讨论行为子类型。

In conclusion, I hope that I have convinced you that the correct terminology to use when discussing the correctness of subclass relationships in object-oriented programming is behavioral subtyping, not substitutability.

总之,我希望我已经说服了您,在讨论面向对象编程中子类关系的正确性时使用的正确术语是行为子类型,而不是可替代性。

翻译自: https://medium.com/@bart.jacobs/liskov-substitution-principle-a-misnomer-3a891c29d359

里斯科夫替换

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是斯科夫替换原则、单一职责原则、开闭原则、德(迪)米特法则、依赖倒转原则、合成复用原则在博客系统中的具体应用: 1. 斯科夫替换原则:在博客系统中,可以定义一个基类或接口,所有的博客类都继承于该基类或实现该接口。这样,无论何时需要使用一个博客对象,都可以使用该基类或接口的引用来引用这个博客对象。 2. 单一职责原则:在博客系统中,每个类都应该只负责一种功能,并将这些功能尽可能地分离开来,避免一个类承担过多的职责。比如,博客文章类只负责文章的相关操作,而不涉及到其它不相关的功能。 3. 开闭原则:在博客系统中,可以使用接口或抽象类来定义可扩展的模块,从而避免对已有代码的修改。这样,当需要添加新的功能时,只需要实现这个接口或继承这个抽象类即可,而不会对已有代码造成影响。 4. 德(迪)米特法则:在博客系统中,可以使用中介者模式或观察者模式来减少对象之间的直接依赖关系,从而降低耦合度。比如,博客文章类可以通过中介者模式来实现与博客评论类之间的交互。 5. 依赖倒转原则:在博客系统中,可以使用依赖注入或反转控制等技术,来实现高层模块与底层模块之间的解耦。比如,博客文章类可以通过依赖注入来获取需要的服务对象,而不需要直接依赖于这些服务对象。 6. 合成复用原则:在博客系统中,可以使用组合模式或装饰器模式等技术来实现代码的复用,并避免继承带来的一些问题。比如,博客文章类可以通过组合模式来实现与博客评论类之间的交互,而不需要通过继承来实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值