It means that the preconditions were strengthened in the subtype? Did we break the LSP?
Yes and yes.
You presented the LSP from a theoretical perspective, but a more practical way to look at the LSP is from the point of view of the client of your API. Let's say you define a function like this:
function reinitializePerson(person: Person) {
person.setAge(0);
}
Due to the implementation of Employee
, this function may fail depending on the concrete instance of Person
. This is the fundamental issue and the reason why the implementation of Employee.setAge
violates the Liskov Substitution PrincipleLSP: you must know the concrete type of the person
object to ensure you can respect the contract of the setAge
method.
In layman's terms, the Liskov Substitution Principle means "Can the child class be used in every context where the parent class is used?", which is no in your case.
How to fix this relationship in order to not break the LSP?
If I take your question in the literal sense, the fix would be not to enforce that the age must be at least 18. Of course, this may or may not make sense in your context. If you cannot loosen this precondition, it is a very strong hint that there is not an inheritance relationship between Person
and Employee
. There is possibly a common interface you can extract out of them, but the first step would be to ensure Employee
does not inherit from Person
.