So the LSP says that if S is a subtype of T, then any instance of S should be able to replace any instance of T without altering any of the desirable properties of that program.
Does that mean that this would fail the LSP because when you do dev_1.print_name()
(substituting the instance of the subclass with that of the base class -ish), you get an unexpected result(failure) because you haven't initialised the name?
@dataclass
class Employee:
name: str
def print_name(self):
print(self.name)
class Developer(Employee):
def __init__(self, work_from_home: bool):
self.work_from_home = work_from_home
dev_1 = Developer(True)
dev_1.print_name()
And the way to fix that would be to change the Developer class to something like this, to be compatible with all the methods of the Employee
class?
class Developer(Employee):
def __init__(self, work_from_home: bool, name:str):
self.work_from_home = work_from_home
super().__init__(name)
You are correct. As your code is written, creating an Employee
will require that its name
attribute be given a value. But as written, your code will not initialize name
when creating a Developer
. Just as you say, this leads to a Developer
object not behaving like an Employee
object, so it violates LSP.
Your solution of adding a constructor that takes a value for the name
attribute does indeed solve the problem. The standard way to get this same behavior is to annotate Developer
as a data class as well so that you get the proper constructor for free:
@dataclass
class Employee:
name: str
def print_name(self):
print(self.name)
@dataclass
class Developer(Employee):
work_from_home: bool
def print_developer(self):
print(f"Developer {self.name} does{'' if self.work_from_home else ' not'} work from home")
def main():
dev_1 = Developer("Jack", False)
dev_1.print_developer()
Result:
Developer Jack does not work from home
You can also treat a Developer
object as an Employee
:
emp_1 = Developer("Jill", True)
emp_1.print_name()
Result:
Jill