I have heard that the Liskov Substitution Principle (LSP) is a fundamental principle of object oriented design. What is it and what are some examples of its use?
相关问题
- how to define constructor for Python's new Nam
- Keeping track of variable instances
- Object.create() bug?
- std::vector of objects / pointers / smart pointers
- Name for a method that has only side effects
相关文章
- 接口B继承接口A,但是又不添加新的方法。这样有什么意义吗?
- NameError: name 'self' is not defined, eve
- Implementation Strategies for Object Orientation
- Check if the Type of an Object is inherited from a
- When to use Interfaces in PHP
- Are default parameters bad practice in OOP?
- How to return new instance of subclass while initi
- In OOP, what is the best practice in regards to us
The Liskov Substitution Principle (LSP, lsp) is a concept in Object Oriented Programming that states:
At its heart LSP is about interfaces and contracts as well as how to decide when to extend a class vs. use another strategy such as composition to achieve your goal.
The most effective way I have seen to illustrate this point was in Head First OOA&D. They present a scenario where you are a developer on a project to build a framework for strategy games.
They present a class that represents a board that looks like this:
All of the methods take X and Y coordinates as parameters to locate the tile position in the two-dimensional array of
Tiles
. This will allow a game developer to manage units in the board during the course of the game.The book goes on to change the requirements to say that the game frame work must also support 3D game boards to accommodate games that have flight. So a
ThreeDBoard
class is introduced that extendsBoard
.At first glance this seems like a good decision.
Board
provides both theHeight
andWidth
properties andThreeDBoard
provides the Z axis.Where it breaks down is when you look at all the other members inherited from
Board
. The methods forAddUnit
,GetTile
,GetUnits
and so on, all take both X and Y parameters in theBoard
class but theThreeDBoard
needs a Z parameter as well.So you must implement those methods again with a Z parameter. The Z parameter has no context to the
Board
class and the inherited methods from theBoard
class lose their meaning. A unit of code attempting to use theThreeDBoard
class as its base classBoard
would be very out of luck.Maybe we should find another approach. Instead of extending
Board
,ThreeDBoard
should be composed ofBoard
objects. OneBoard
object per unit of the Z axis.This allows us to use good object oriented principles like encapsulation and reuse and doesn’t violate LSP.
There is a check list to determine whether or not you are violating Liskov.
Check list:
History Constraint: When overriding a method you are not allowed to modify an un-modifiable property in the base class. Take a look at these code and you can see Name is defined to be un-modifiable (private set) but SubType introduces new method that allows modifying it (through reflection):
There are 2 others items: Contravariance of method arguments and Covariance of return types. But it is not possible in C# (I'm a C# developer) so I dont care about them.
Reference:
In a very simple sentence, we can say:
The child class must not violate its base class characteristics. It must be capable with it. We can say it's same as subtyping.
I encourage you to read the article: Violating Liskov Substitution Principle (LSP).
You can find there an explanation what is the Liskov Substitution Principle, general clues helping you to guess if you have already violated it and an example of approach that will help you to make your class hierarchy be more safe.
When I first read about LSP, I assumed that this was meant in a very strict sense, essentially equating it to interface implementation and type-safe casting. Which would mean that LSP is either ensured or not by the language itself. For example, in this strict sense, ThreeDBoard is certainly substitutable for Board, as far as the compiler is concerned.
After reading up more on the concept though I found that LSP is generally interpreted more broadly than that.
In short, what it means for client code to "know" that the object behind the pointer is of a derived type rather than the pointer type is not restricted to type-safety. Adherence to LSP is also testable through probing the objects actual behavior. That is, examining the impact of an object's state and method arguments on the results of the method calls, or the types of exceptions thrown from the object.
Going back to the example again, in theory the Board methods can be made to work just fine on ThreeDBoard. In practice however, it will be very difficult to prevent differences in behavior that client may not handle properly, without hobbling the functionality that ThreeDBoard is intended to add.
With this knowledge in hand, evaluating LSP adherence can be a great tool in determining when composition is the more appropriate mechanism for extending existing functionality, rather than inheritance.
Robert Martin has an excellent paper on the Liskov Substitution Principle. It discusses subtle and not-so-subtle ways in which the principle may be violated.
Some relevant parts of the paper (note that the second example is heavily condensed):