Questions tagged «liskov-substitution»

有关面向对象设计中Liskov替换原理的问题。

10
这是否违反了《里斯科夫换人原则》?
假设我们有一个Task实体列表和一个ProjectTask子类型。任务可以随时关闭,除非ProjectTasks状态为“已启动”的任务无法关闭。用户界面应确保关闭启动选项ProjectTask永远不会可用,但是域中存在一些保护措施: public class Task { public Status Status { get; set; } public virtual void Close() { Status = Status.Closed; } } public class ProjectTask : Task { public override void Close() { if (Status == Status.Started) throw new Exception("Cannot close a started Project Task"); base.Close(); } } 现在,在调用Close()Task时,如果调用ProjectTask处于启动状态,则有可能失败,而如果它是基本Task,则调用不会失败。但这是业务需求。它应该失败。可以认为这违反了Liskov替代原则吗?

8
LSP vs OCP / Liskov换人VS开盘关闭
我试图理解OOP的SOLID原则,并且得出的结论是LSP和OCP有一些相似之处(如果不多说的话)。 开放/封闭原则指出“软件实体(类,模块,功能等)应为扩展而开放,但为修改而封闭”。 LSP在简单的话指出,任何情况下Foo可以用的任何实例来代替Bar它源自Foo并计划将工作同样非常的方式。 我不是专业的OOP程序员,但在我看来,只有在Bar派生自LSP Foo不会更改其中任何内容而只能扩展它的情况下,LSP才是可能的。这意味着特别是程序LSP仅在OCP为true时才为true,而OCP仅在LSP为true时才为true。那意味着他们是平等的。 如果我错了纠正我。我真的很想了解这些想法。非常感谢您的回答。

3
子类和子类型之间有什么区别?
关于这个问题的最高的答案是关于Liskov替代原理的努力,竭力区分子类型和子类。这也说明有些语言将两者混为一谈,而另一些则没有。 对于我最熟悉的面向对象语言(Python,C ++),“类型”和“类”是同义词。对于C ++,在子类型和子类之间进行区分意味着什么?举例来说,这Foo是的子类,但不是子类型FooBase。如果foo是的实例Foo,则此行: FooBase* fbPoint = &foo; 不再有效?

14
是否有某种语言或设计模式允许“删除”类层次结构中的对象行为或属性?
传统类层次结构的一个众所周知的缺点是,在对现实世界建模时,它们是不好的。例如,尝试用类来表示动物的种类。这样做确实存在几个问题,但是我从未见过的解决方案是,当子类“丢失”超类中定义的行为或属性时,例如企鹅无法飞行(有也许是更好的例子,但这是我想到的第一个)。 一方面,您不想为每个属性和行为定义一些标志来指定它们是否全部存在,并在访问该行为或属性之前每次对其进行检查。您只想说在Bird类中,鸟类可以简单,清晰地飞行。但是,如果以后可以定义“例外”而不必到处使用一些可怕的骇客,那就太好了。当系统生产一段时间后,通常会发生这种情况。您突然发现一个根本不适合原始设计的“例外”,并且您不想更改代码的很大一部分来容纳它。 因此,是否有一些语言或设计模式可以干净地处理此问题,而无需对“超类”以及使用它的所有代码进行重大更改?即使解决方案仅处理特定情况,也可以将多个解决方案一起形成一个完整的策略。 经过更多思考,我意识到我忘记了《里斯科夫替代原则》。这就是为什么你不能这样做。假设您为所有主要的“功能组”定义了“特征/接口”,则可以在层次结构的不同分支中自由实现特征,例如“飞翔”特征可以由Birds以及某些特殊的松鼠和鱼来实现。 因此,我的问题可能是“我如何取消实施特质?” 如果您的超类是Java可序列化的Java,则即使您无法对状态进行序列化(例如,如果您包含“ Socket”),您也必须也是一个。 一种方法是始终从一开始就成对定义所有特征:Flying和NotFlying(如果不进行检查,则会抛出UnsupportedOperationException)。非特征不会定义任何新接口,可以简单地进行检查。听起来像是“便宜”的解决方案,特别是如果从一开始就使用它。


5
具有后备情况的特殊情况是否违反《里斯科夫替代原则》?
假设我有一个FooInterface具有以下签名的接口: interface FooInterface { public function doSomething(SomethingInterface something); } 还有一个ConcreteFoo实现该接口的具体类: class ConcreteFoo implements FooInterface { public function doSomething(SomethingInterface something) { } } ConcreteFoo::doSomething()如果它传递了一种特殊类型的SomethingInterface对象(例如称为SpecialSomething),我想做一些独特的事情。 如果我加强方法的先决条件或引发新的异常,则绝对是LSP违规,但是如果我SpecialSomething在为通用SomethingInterface对象提供后备时又对特殊情况的对象进行了处理,这是否仍会违反LSP ?就像是: class ConcreteFoo implements FooInterface { public function doSomething(SomethingInterface something) { if (something instanceof SpecialSomething) { // Do SpecialSomething magic } else { // Do generic …



3
国家模式是否违反《里斯科夫替代原则》?
该图像取自“ 应用域驱动的设计和模式:带有C#和.NET中的示例” 这是状态模式的类图,其中a SalesOrder在生命周期内可以具有不同的状态。在不同状态之间仅允许某些转换。 现在,OrderState该类是一个abstract类,并且其所有方法都继承为其子类。如果我们将子类视为Cancelled最终状态,不允许将其转换为其他任何状态,则必须对override此类中的所有方法进行抛出。 现在,这不是违反Liskov的替代原则吗,因为sublcass不应改变父级的行为吗?将抽象类更改为接口是否可以解决此问题? 如何解决?


2
现实世界-Liskov替代原理
背景:我正在开发一个消息传递框架。该框架将允许: 通过服务总线发送消息 订阅消息总线上的队列 订阅消息总线上的主题 我们目前正在使用RabbitMQ,但我知道我们将在不久的将来迁移到Microsoft Service Bus(在Premise上)。 我计划创建一组接口和实现,以便当我们转向ServiceBus时,我只需要提供一个新的实现而无需修改任何客户端代码(即发布者或订阅者)。 这里的问题是RabbitMQ和ServiceBus不能直接翻译。例如,RabbitMQ依赖于交换和主题名称,而ServiceBus全部与命名空间和队列有关。同样,在ServiceBus客户端和RabbitMQ客户端之间没有公共接口(例如,两者都可以具有IConnection,但是接口是不同的-不同于公共名称空间)。 因此,就我而言,我可以创建如下界面: public interface IMessageReceiver{ void AddSubscription(ISubscription subscriptionDetails) } 由于这两种技术的不可翻译特性,上述接口的ServiceBus和RabbitMQ实现具有不同的要求。因此,我的IMessageReceiver的RabbitMq实现可能如下所示: public void AddSubscription(ISubscription subscriptionDetails){ if(!subscriptionDetails is RabbitMqSubscriptionDetails){ // I have a problem! } } 对我来说,上面的线打破了李斯科夫的可替代性规则。 我考虑过将其翻转,以便订阅可以接受IMessageConnection,但是RabbitMq订阅再次需要RabbitMQMessageConnection的特定属性。 因此,我的问题是: 我是否认为这会破坏LSP? 我们是否同意在某些情况下这是不可避免的,还是我错过了一些东西? 希望这很清楚,而且是话题!


1
如何在继承层次结构中验证Liskov替换原理?
灵感来自这个答案: 里氏替换原则要求 是 前提条件不能在子类型中得到加强。 子条件不能弱化后置条件。 超类型的不变量必须保留在子类型中。 历史记录约束(“历史记录规则”)。只能通过对象的方法(封装)将其视为可修改的对象。由于子类型可能会引入父类型中不存在的方法,因此这些方法的引入可能会导致子类型中状态不允许在父类型中发生变化。历史记录约束禁止这样做。 我希望有人能发布违反这4点的类层次结构,以及如何相应地解决它们。 我正在寻找出于教育目的的详细说明,以了解如何识别层次结构中的四个点以及解决该问题的最佳方法。 注意: 我希望发布代码示例供人们使用,但是问题本身是关于如何识别错误的层次结构的:)

2
堆栈扩展LinkedList。是否违反了Liskov替代原则?
存在具有诸如add_first(),add_last(),add_after(),remove_first(),remove_last()和remove()之类的函数的LinkedList类。 现在有一个Stack类,提供了push(),pop(),peek()或top()等功能,并且为了实现这些方法,它扩展了LinkedList类方法。这是否违反了《里斯科夫替代原则》? 例如,考虑在add_after()情况下将节点添加到链接列表的第n个位置的情况。这可以在基类中完成,但不能在Stack类中完成。此处的后置条件是否已减弱,或者您是否修改add_after()方法以将其添加到堆栈顶部? 另外,如果不违反,这是不好的设计吗?以及如何使用LinkedList类实现Stack功能?

6
通过逆转关系可以解决圆椭圆问题吗?
具有CircleextendEllipse违反了Liskov替换原理,因为它修改了后置条件:即,可以独立设置X和Y来绘制椭圆,但是对于圆,X必须始终等于Y。 但是,不是因为Circle是Ellipse的子类型引起的问题吗?我们不能扭转这种关系吗? 因此,Circle是超类型-它只有一个方法setRadius。 然后,椭圆通过添加setX和扩展Circle setY。调用setRadiusEllipse将同时设置X和Y-意味着保留setRadius的后置条件,但是现在您可以通过扩展接口独立设置X和Y。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.