标签: liskov-substitution-principle

抽象属性是否违反Liskov替换原则?

假设我有一个抽象类,如:

public abstract class Pet {
    private final String name;
    public Pet(String name) { 
        this.name = name 
    };

    public abstract boolean getsSpecialTreatment();
}

public final class Dog extends Pet {
    @Override public boolean getsSpecialTreatment() { return true; }
}

public final class Cat extends Pet {
    @Override public boolean getsSpecialTreatment() { return false; }
}
Run Code Online (Sandbox Code Playgroud)

我的程序将Pet根据是否设置特殊处理标志来区别对待s.我的问题是,这是否违反Liskov替代原则,该原则指出:

[...]在计算机程序中如果S是T的子类型,那么T类型的对象可以用S类型的对象替换而不改变该程序的任何所需属性(正确性,执行的任务)等).

java oop liskov-substitution-principle

8
推荐指数
2
解决办法
1030
查看次数

层次结构违反Liskov - 那又怎样?

我正在使用违反Liskov替换原则的API:它抛出自己的Exception类型,扩展Exception,但将来自基类的异常消息放入新的ErrorCode字段中,并将自己的(无用的)消息放入Message字段中.因此,要显示正确的消息,我需要将Exception转换为DerivedException类型并使用ErrorCode字段.如果我将它视为异常对象,我会得到错误的消息.

现在这让我感觉很震撼,但是它很容易解决:我可以捕获DerivedException并将其用作程序员的预期.所以我的问题是:利斯科夫原则有什么大不了的?使用违反原则的层次结构,人们可能会遇到哪些实际问题?

c# inheritance liskov-substitution-principle

7
推荐指数
1
解决办法
258
查看次数

我可以实现一系列可重用的测试来测试接口的实现吗?

我在C#中编写了一系列集合类,每个集合类都实现了类似的自定义接口.是否可以为接口编写单个单元测试集合,并在几个不同的实现上自动运行它们?我想避免每个实现的任何重复的测试代码.

我愿意研究任何框架(NUnit等)或Visual Studio扩展来实现这一目标.


对于那些希望这样做的人,我根据avandeursen公认的解决方案发布了我的具体解决方案作为答案.

.net unit-testing liskov-substitution-principle visual-studio-2010

7
推荐指数
1
解决办法
1172
查看次数

Liskov替换原则和使用继承类的正确方法

我有一些处理程序("控制器")类,他们可以以某种方式处理项目:

interface IHandler
{
    public function execute(Item $item);
}

class FirstHandler implements IHandler
{
    public function execute(Item $item) { echo $item->getTitle(); }
}

class SecondHandler implements IHandler
{
    public function execute(Item $item) { echo $item->getId() . $item->getTitle(); }
}

class Item
{
    public function getId() { return rand(); }
    public function getTitle() { return 'title at ' . time(); }
}
Run Code Online (Sandbox Code Playgroud)

但是我需要在子Item类中添加一些新功能:

class NewItem extends Item
{
    public function getAuthor() { return 'author ' . rand(); }
}
Run Code Online (Sandbox Code Playgroud)

并在SecondHandler中使用它

class …
Run Code Online (Sandbox Code Playgroud)

php architecture oop inheritance liskov-substitution-principle

7
推荐指数
1
解决办法
503
查看次数

为什么PHP中的LSP违规有时是致命的,有时是警告?

此LSP违规会引发致命错误:

abstract class AbstractService { }
abstract class AbstractFactory { abstract function make(AbstractService $s); }
class ConcreteService extends AbstractService { }
class ConcreteFactory extends AbstractFactory { function make(ConcreteService $s) {} }
Run Code Online (Sandbox Code Playgroud)

此LSP违规也会引发致命错误:

interface AbstractService { }
interface AbstractFactory { function make(AbstractService $s); }
class ConcreteService implements AbstractService { }
class ConcreteFactory implements AbstractFactory { function make(ConcreteService $s) {} }
Run Code Online (Sandbox Code Playgroud)

虽然这种LSP违规只会引发警告:

class Service { }
class Factory { function make(Service $s) {} }
class MyService …
Run Code Online (Sandbox Code Playgroud)

php liskov-substitution-principle

7
推荐指数
1
解决办法
338
查看次数

为什么“李斯科夫替代原则”需要论证是对立的?

Liskov替代原理对派生类中的方法签名施加的规则之一是:

子类型中方法参数的矛盾性。

如果我理解正确的话,就是说派生类的重写函数应允许使用自变量(超类型自变量)。但是,我不明白这条规则背后的原因。由于LSP主要讨论动态地将类型与那里的子类型(而不是超类型)进行绑定以实现抽象,因此让超类型作为派生类中的方法参数对我来说是很困惑的。我的问题是:

  • 为什么LSP在派生类的覆写功能中允许/要求使用Contravariant参数?
  • 协方差规则如何有助于实现数据/过程抽象?
  • 在现实世界中,是否有任何示例需要将反参数传递给派生类的重写方法?

liskov-substitution-principle contravariance

7
推荐指数
2
解决办法
1028
查看次数

类型 - 子类型关系.有些东西似乎不清楚

我正在阅读面向对象编程语言的类的一些幻灯片,并进入类型子类型定义:

Barbara Liskov,"数据抽象和层次结构",SIGPLAN Notices,23,5,1988年5月:

这里需要的是类似下面的替换属性:如果对于类型S的每个对象o_s,存在类型为T的对象o_T,使得对于以T表示的所有程序P
,当o_S被替换时,P的行为不变.对于o_T,则S是T的子类型

然后它举个例子:

Point = {x:Integer,y:Integer}
PositivePoint = {x:Positive,y:Positive}
其中Positive = {k:Integer | k> 0}

我们可以说PositivePoint≤Point吗?

是的,因为PositivePoint类型的元素可能总是在Point术语中定义的程序中替换Point类型的元素!

现在......对我而言,它似乎应该完全相反:Point≤DealgePoint因为我无法在使用带负坐标的Point的程序中使用PositivePoint,而我可以反过来.

我怀疑,如果语法是Type ? Sub-typeSub-Type ? Type,但声明似乎更清楚,什么是错的呢?


编辑

只是为了让事情变得更容易,问题是:你能说这PositivePoint是一个子类型Point吗?为什么?


第二次编辑

我在这里报告我在评论中写的内容,希望它能让我的问题更加清晰:

假设程序必须绘制从Point(-100,-100)到Point(100,100 )的方形图.如果你使用类型会发生什么 PositivePoint?该计划的行为会保持不变吗?它不会.这种"不变的行为"是我唯一没有得到的.如果子类型的定义只是inheriting and overriding来自其他类型,那就没关系,但似乎并非如此.

oop liskov-substitution-principle

6
推荐指数
1
解决办法
178
查看次数

SOLID Essentials遗漏点?

我已经阅读了很多这方面的文章,但我还有两个问题.

问题#1 - 关于依赖性倒置:

它声明高级类不应该依赖于低级类.两者都应该取决于抽象.抽象不应该依赖于细节.细节应取决于抽象.

例如 :

public class BirthdayCalculator
{
    private readonly List<Birthday> _birthdays;

    public BirthdayCalculator()
    {
        _birthdays = new List<Birthday>();// <----- here is a dependency
    }
...
Run Code Online (Sandbox Code Playgroud)

修复:把它放在一个ctor.

public class BirthdayCalculator
{
    private readonly IList<Birthday> _birthdays;

    public BirthdayCalculator(IList<Birthday> birthdays) 
    {
        _birthdays = birthdays;
    }
Run Code Online (Sandbox Code Playgroud)
  • 如果它将在ctor中 - 我将不得不每次使用该课程时发送它.所以在打电话给BirthdayCalculator班级时我必须保留它.它可以这样做吗?

  • 我可以争辩说,在修复之后,仍然 - IList<Birthday> _birthdays不应该在那里(Birthdayin IList) - 但它应该是IList<IBirthday>.我对吗 ?

问题#2 - 关于利斯科夫换人:

派生类必须可替代其基类

或更准确:

设q(x)是关于类型T的对象x可证明的属性.对于类型S的对象y,q(y)应该为真,其中S是T的子类型.

(已经读过这个)

例如:

public …
Run Code Online (Sandbox Code Playgroud)

c# design-patterns liskov-substitution-principle inversion-of-control solid-principles

6
推荐指数
1
解决办法
295
查看次数

`PartialFunction extends Function` 是否违反了 LSP?

Liskov 替换原则指出

如果S是 的子类型T,则类型的对象T可以替换为类型的对象,S而不会改变该程序的任何所需属性。

但是,在 Scala 中,PartialFunction并非在所有情况下都适用/定义。

trait Function1 {
  def apply(x: A): R
}

trait PartialFunction extends Function1 {
  def apply(x: A): R
  def isDefinedAt(x: A): Boolean
}
Run Code Online (Sandbox Code Playgroud)

如果将 aPartialFunction应用于未定义的值,您将收到异常。

PartialFunction在 Scala 中创建一个的方便方法是使用模式匹配。这样做,您会收到一个MatchError未定义的值。

val fn:Function1[String, Int] = s => s.length

val pf:PartialFunction[String, Int] = {
  case "one" => 3
}

def program(f:Function1[String, Int], s:String):(Boolean, Int) = (
  f.isInstanceOf[Function1[String, Int]], f(s)
)

program(fn, "one") == …
Run Code Online (Sandbox Code Playgroud)

scala liskov-substitution-principle

6
推荐指数
1
解决办法
129
查看次数

更好的设计以避免违反里氏替换原则

我遇到了里氏替换原则的问题,并且不太确定解决它的最佳方法是什么。

有问题的代码

class BaseModel:
    def run(self, base_model_input: BaseModelInput) -> BaseModelOutput:
        """Throws NotImplemented or @abstractmethod"""
        pass

class SpecificModel(BaseModel):
    def run(self, specific_input: SpecificModelInput) -> SpecificModelOutput:
        # do things...
Run Code Online (Sandbox Code Playgroud)

我很清楚为什么这不是一个很好的代码,以及为什么它违反了里氏替换原则。我想知道如何更好地设计我的系统以避免这个问题。

从根本上讲,我有一个BaseModel类似于接口的类,提供一些run扩展类必须实现的方法。但是扩展类还处理特定的输入/输出,它们也是基本输入/输出类的扩展(继承SpecificModelInputBaseModelInput添加一些字段和功能,与输出相同)

这里更好的方法是什么?

python design-patterns liskov-substitution-principle python-3.x python-typing

6
推荐指数
1
解决办法
945
查看次数