【问题标题】: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,因为它不符合前置条件

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

【问题讨论】:

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

标签: java solid-principles contract liskov-substitution-principle


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

  2. 不适用

  3. 在我看来:由于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 调用验证也是如此。两者的行为相同。
猜你喜欢
  • 2015-01-01
  • 2020-02-03
  • 2012-01-12
  • 2017-07-04
  • 1970-01-01
  • 1970-01-01
  • 2011-09-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多