下面的例子是不是违反了 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】:
不,Person
和 Employee
的实例在调用 validate
时具有完全相同的行为范围。也就是说,调用它会导致IllegalArgumentException
被抛出,或者它不会,因此没有代码调用validate
并正确处理在Person
上调用它的结果将无法正确处理结果在Employee
上调用它。
不适用
在我看来:由于IllegalArgumentException
和NullPointerException
都是未经检查的异常,它们不构成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 替换原则?的主要内容,如果未能解决你的问题,请参考以下文章
你能用一个好的 C# 例子来解释 Liskov 替换原则吗? [关闭]
面向对象设计原则 里氏替换原则(Liskov Substitution Principle)
"围观"设计模式--里氏替换原则(LSP,Liskov Substitution Principle)