00:38 As a child inherits its parents’ members, as long as the child doesn’t break anything, you can be compliant with this principle by default. It’s related to a similar concept known as design by contract—the interface of a thing is how you interact with it—and as children get the same interface as their parent, they should be substitutable.
By moving that into the
FlyingBird class, this code no longer violates the LSP. If I add a
Robin, it inherits
FlyingBird, and whether I use an
Eagle or a
Robin, my interface is consistent. If I add a
Penguin, it inherits directly from
Bird, becoming a sibling of
Ostrich, and the fact that it can’t fly doesn’t break the interface.
01:59 A common cause of violating the LSP is overthinking class hierarchies. It’s real easy to say, well, an ostrich is a bird, and an eagle is a bird, so I must need a bird class. In reality, you should be more concerned about the behavior of the class—i.e., what its methods are—than the physicality of the class.
Some rules to consider are operations on a parent should be valid on a child—the
.fly() example I just showed you. Methods on the child should take at least the same arguments as that of the parent. You can add more args, but the base should be the same.
03:23 The problem here is those are both attributes. Some code somewhere might modify the width. Passing a square to that code breaks the idea of a square. Getting caught up in the is-a rather than focusing on the operations you wish to perform on the class leads you down a troubled road.
03:45 This course is based on a Real Python article. In it, the author does some fancier Python, making the argument that the LSP is being violated. Let me show you what he did, and then we can argue about the argument.
I’ve still got my square based on my rectangle, but now
.__setattr__() is overridden. If you attempt to change the width or the height, they both get changed to the new value. This is a subtle thing.
I could argue it no longer violates the LSP. Anything you do to this square maintains its squareness, and because it’s done through the magic of
.__setattr__(), anyone consuming the square doesn’t have to be aware of this. Now, I’m not arguing this is a good design, but for the life of me, I can’t come up with a counterexample where any operation that worked on the rectangle wouldn’t now work on the square.
Become a Member to join the conversation.