Skip to content

Commit

Permalink
Typo Fixes for LSP Article (#608)
Browse files Browse the repository at this point in the history
  • Loading branch information
langsonzhang authored Mar 20, 2024
1 parent 56df193 commit 1c6a3e8
Showing 1 changed file with 5 additions and 4 deletions.
9 changes: 5 additions & 4 deletions Topics/Development_Process/LSP.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ We could try extracting out the logic of setting the width and height simultaneo



## 2. The Principles in The LSP
## 3. The Principles in The LSP
The subtype requirement is an incredibly powerful tool for reasoning about our programs, but how does the LSP help us guide our programming to satsify it? The LSP imposes requirements[^1] on method signatures common across many typed languages:

1. **Contravariance of parameter types in the subtype** – if parameters **P** are permissible in method ***f*** of a type, then **P** is also be permissible in ***f*** of any subtype. Equivalently:
Expand All @@ -38,11 +38,12 @@ but also imposes additional behavioural requirements[^1]:

Requirements 1 to 6 are straightforward, so we won't discuss them. But the reader should convince themself that they are important, and necessary to satisfy the subtype requirement. We'll discuss requirement 7 as it's the novel requirment introduced in the LSP, and has important implications on how subtyping/inheritance should be used. The Wikipedia article illustrates a nice example of a violation of the History Constraint in the case of subtyping between mutable and immutable objects, but we'll illustrate a scenario that is simpler and probably more relatable.

If you've learnt about OOP, you may have been told that the use of mutable public instance variables in a class is generally not a good idea, and that you should instead create getter and setter methods for it. Whether or not you agree with this practice, using mutable public instance variables is problematic in the context of the LSP and using subtyping/inheritance: not only is it possible to freely mutate the state of the object, hence violating the History Constraint, but you may also break invariants which are assumed by the implementation of the methods in the supertype, causing catastrophic failure of your program! On the contrary, if all your mutable instance variables are private (meaning state can only be modified through exposed methods of the supertype), then its *impossible* for the History Constraint to be violated. Note that this does not mean that one *must* declare all their instance variables this way to satisfy the History Constraint, it's just that its a very simple and easy way to ensure that they do. Of course, one could be incredibly disciplined and meticulous in their programming, but at that point they may as well avoid subtyping/inheritance as they'll shoulder all the associated complexity and gain little to none of the benefits.

A side note: the satisfaction of the subtype requirement is undecidable, meaning no computer program, hence no compiler or linter or any static analysis tool, can figure out whether or not its violated in general.
If you've learnt about OOP, you may have been told that the use of mutable public instance variables in a class is generally not a good idea, and that you should instead create getter and setter methods for it. Whether or not you agree with this practice, using mutable public instance variables is problematic in the context of the LSP and using subtyping/inheritance: not only is it possible to freely mutate the state of the object, hence violating the History Constraint, but you may also break invariants which are assumed by the implementation of the methods in the supertype, causing catastrophic failure of your program! On the contrary, if all your mutable instance variables are private (meaning state can only be modified through exposed methods of the supertype), then it's *impossible* for the History Constraint to be violated. Note that this does not mean that one *must* declare all their instance variables this way to satisfy the History Constraint, it's just that it's a very simple and easy way to ensure that they do. Of course, one could be incredibly disciplined and meticulous in their programming, but at that point they may as well avoid subtyping/inheritance as they'll shoulder all the associated complexity and gain little to none of the benefits.

## 3. Conclusion
A side note: the satisfaction of the subtype requirement is undecidable, meaning no computer program, hence no compiler or linter or any static analysis tool, can figure out whether or not it's violated in general.

## 4. Conclusion
We discussed the notion of subtyping proposed by the LSP and its utility for reasoning about our programs. We looked at an example of how the subtype requirment can be violated in a simple use of class inheritance. We discussed the actual principles/requirements imposed by the LSP to guide our programming to satisfy the subtyping requirement. And finally, we looked at an example of how the novel History Constraint introduced by the LSP impacts how we do object-oriented programming. Hopefully this writeup gave a little bit more insight into the implications of the LSP and motivated its ideas for some readers. Of course, there's much more to learn about the LSP than what we've discussed here. The Wikipedia article and the original paper[^2] by Liskov and Wing are good places to go next.


Expand Down

0 comments on commit 1c6a3e8

Please sign in to comment.