下面的例子是不是违反了 Liskov 替换原则?

Posted

技术标签:

【中文标题】下面的例子是不是违反了 Liskov 替换原则?【英文标题】:Does the example below violate Liskov Substitution Principal?下面的例子是否违反了 Liskov 替换原则? 【发布时间】:2021-11-17 07:13:44 【问题描述】:

谁能告诉我下面的例子是否违反了 LSP?

我有一个例子:

public class Person 
    private String name;
    private Integer age;

    public Person(String name, Integer age) 
        this.name = name;
        this.age = age;
    

    public void validate() 
        if (age == null || age < 0) 
            throw new IllegalArgumentException("Age can not be null");
        
    

和子类:

public class Employee extends Person 
    private String employeeCode;

    public Employee(String name, Integer age, String employeeCode) 
        super(name, age);
        this.employeeCode = employeeCode;
    

    @Override
    public void validate() 
        super.validate();
        if (employeeCode == null) 
            throw new IllegalArgumentException("Employee code can not be null");
        
    

和主类:

public class Main 
    public static void main(String[] args) 
        Person person = new Person("Person", 10);
        validate(person); // will be ok. does not throw any exception

        Person employee = new Employee("Employee", 30, null);
        validate(employee); // will be throw IllegalArgumentException because subtype's employee code is null
    

    public static void validate(Person p) 
        p.validate();
    



在此示例中,子类添加名为 employeeCode 的新属性并覆盖方法 validate 额外检查它自己的财产employeeCode

在 main 方法中,我创建了 2 个对象。第一个是Person 类型的对象,第二个是Employee 类型的对象。

验证人时没问题,因为所有前提条件都可以 但是对于员工来说,它会抛出一个IllegalArgumentException,因为它不符合前置条件

    Employee 是否因在 employeeCode 上添加新验证而违反 LSP? 如果 1 是 yes,我该如何重构它以避免违反 LSP? 如果 1 是 no,如果我将异常从 IllegalArgumentException("Employee code can not be null") 更改为另一个异常 NullPointerException。那么它是否因为在子类型中引入了新的异常类型(哪个超类型没有)而违反了 LSP?

【问题讨论】:

为了回答这个问题,我们需要知道validate()方法的完整契约。在像 Java 这样的语言中,方法签名只是contract 的一部分。该方法的前置条件、后置条件和不变量是什么? @jaco0646 此方法的不变量是:年龄不能为空或 【参考方案1】:

    不,PersonEmployee 的实例在调用 validate 时具有完全相同的行为范围。也就是说,调用它会导致IllegalArgumentException 被抛出,或者它不会,因此没有代码调用validate 并正确处理在Person 上调用它的结果将无法正确处理结果在Employee 上调用它。

    不适用

    在我看来:由于IllegalArgumentExceptionNullPointerException 都是未经检查的异常,它们不构成validate 合同的一部分,它有权抛出RuntimeException 的任何子类。更好的设计将throws ValidationException 作为validate 签名的一部分。

正如@jaco0646 在他们上面的评论中所说,Java 不允许您正式指定有关方法的所有内容。

假设我编写了Person 的另一个子类,并决定我的validate() 实现实际上将通过将它们设置为默认值来修复任何无效值。由于我们不希望 validate() 方法改变对象(即使我们没有正式的说法),这将违反 LSP。

【讨论】:

你的意思是说所有继承自超类的属性的行为都不会改变(在这个例子中年龄和名字来自 Person)?当使用employeeCode 为null 验证Employee 时,它​​会抛出一个异常它是否像本文所说的那样破坏应用程序stackify.com/solid-design-liskov-substitution-principle 见上面的说明 “当使用employeeCode 为null 验证员工时,它会抛出异常”,在Person 上使用null age 调用验证也是如此。两者的行为相同。

以上是关于下面的例子是不是违反了 Liskov 替换原则?的主要内容,如果未能解决你的问题,请参考以下文章

敏捷软件开发 – LSP Liskov替换原则

Liskov 替换原则和 PHP 接口

你能用一个好的 C# 例子来解释 Liskov 替换原则吗? [关闭]

软件设计----LisKov替换原则(LSP)

面向对象设计原则 里氏替换原则(Liskov Substitution Principle)

&quot;围观&quot;设计模式--里氏替换原则(LSP,Liskov Substitution Principle)