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

8

Suppose I have an abstract class like:

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; }
}

我的程序将根据特殊处理标志对Pet进行不同的处理。我的问题是,这是否违反了Liskov替换原则,该原则规定:
[...]在计算机程序中,如果S是T的子类型,则可以用S类型的对象替换T类型的对象,而不会改变该程序的任何可取属性(正确性,执行任务等)。
4个回答

6
在这种情况下,那些类的用户可能会编写以下内容:
...
if (pet.getsSpecialTreatment()) {
    // special treatment
    ...
} else {
    // normal treatment
    ...
}
...

这段代码在两种情况下都能正常工作,因此不会违反LSP。但是,如果您有...
public class UnknownAnimal extends Pet {
    @Override public boolean getsSpecialTreatment() {
        throw new UnsupportedOperationException("Unknown species"); 
    }
}

如果您使用UnknownAnimal实例,那么您将违反LSP原则,因为现有的代码将会出现错误。


1
即使最后一个例子并不总是违反LSP原则。这取决于预期的设计。尽管您更有可能希望声明一个检查异常或在API文档中明确说明该方法可能引发某些异常。 - Nicolas Bousquet

4

无论何时在程序中调用此方法,后续的决策都将基于返回值,就像其他任何方法一样。由于该方法的存在本质上不应使程序对其结果做出任何假设。因此,更改此方法返回的值不应更改程序的属性。


1

首先,我强烈反对你歧视猫!

现在,当程序员调用所谓的"Liskov替换原则"时,他们并不是真正从学术意义上讨论它。我们必须使用一些非正式、粗俗、变形的方式来使用它。

那是什么意思?我发现这只是要求子类必须符合超类设定的契约。所以这真的很无聊。人们只是为了装逼而引用这个短语。


我个人同意这个反对意见。但是,我认为像软件模式、"最佳实践"一样的L.S.P.必须以实际的方式应用,而不仅仅是作为"学术 snob"。 - umlcat
1
我不在乎学术上的“LSP”。我们应该停止假装自己是计算机科学家。 - irreputable

0

这取决于合同。也就是说,使用您的类的代码必须获得一致的行为,无论使用您类型的哪个派生。

如果合同规定“getSpecialTreatment”始终返回true,则您在派生类中违反了该规定。

如果合同规定“getSpecialTreatment”返回一个布尔值来确定blabla,则您不会违反LSP。

如果引入了基类中不存在的附加约束,则可能违反LSP。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接