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
Some addendum:
I wonder why didn't anybody write about the Invariant , preconditions and post conditions of the base class that must be obeyed by the derived classes. For a derived class D to be completely sustitutable by the Base class B, class D must obey certain conditions:
So the derived must be aware of the above three conditions imposed by the base class. Hence, the rules of subtyping are pre-decided. Which means, 'IS A' relationship shall be obeyed only when certain rules are obeyed by the subtype. These rules, in the form of invariants, precoditions and postcondition, should be decided by a formal 'design contract'.
Further discussions on this available at my blog: Liskov Substitution principle
An important example of the use of LSP is in software testing.
If I have a class A that is an LSP-compliant subclass of B, then I can reuse the test suite of B to test A.
To fully test subclass A, I probably need to add a few more test cases, but at the minimum I can reuse all of superclass B's test cases.
A way to realize is this by building what McGregor calls a "Parallel hierarchy for testing": My
ATest
class will inherit fromBTest
. Some form of injection is then needed to ensure the test case works with objects of type A rather than of type B (a simple template method pattern will do).Note that reusing the super-test suite for all subclass implementations is in fact a way to test that these subclass implementations are LSP-compliant. Thus, one can also argue that one should run the superclass test suite in the context of any subclass.
See also the answer to the Stackoverflow question "Can I implement a series of reusable tests to test an interface's implementation?"
A great example illustrating LSP (given by Uncle Bob in a podcast I heard recently) was how sometimes something that sounds right in natural language doesn't quite work in code.
In mathematics, a
Square
is aRectangle
. Indeed it is a specialization of a rectangle. The "is a" makes you want to model this with inheritance. However if in code you madeSquare
derive fromRectangle
, then aSquare
should be usable anywhere you expect aRectangle
. This makes for some strange behavior.Imagine you had
SetWidth
andSetHeight
methods on yourRectangle
base class; this seems perfectly logical. However if yourRectangle
reference pointed to aSquare
, thenSetWidth
andSetHeight
doesn't make sense because setting one would change the other to match it. In this caseSquare
fails the Liskov Substitution Test withRectangle
and the abstraction of havingSquare
inherit fromRectangle
is a bad one.Y'all should check out the other priceless SOLID Principles Motivational Posters.
Likov's Substitution Principle states that if a program module is using a Base class, then the reference to the Base class can be replaced with a Derived class without affecting the functionality of the program module.
Intent - Derived types must be completely substitute able for their base types.
Example - Co-variant return types in java.
I see rectangles and squares in every answer, and how to violate the LSP.
I'd like to show how the LSP can be conformed to with a real world example :
This design conforms to the LSP because the behavior remains unchanged regardless of the implementation we choose to use.
And yes, you can violate LSP in this configuration doing one simple change like so :
Now the subtypes cannot be used the same way since they don't produce the same result anymore.
I guess everyone kind of covered what LSP is technically: You basically want to be able to abstract away from subtype details and use supertypes safely.
So Liskov has 3 underlying rules:
Signature Rule : There should be a valid implementation of every operation of the supertype in the subtype syntactically. Something a compiler will be able to check for you. There is a little rule about throwing fewer exceptions and being at least as accessible as the supertype methods.
Methods Rule: The implementation of those operations is semantically sound.
Properties Rule : This goes beyond individual function calls.
All these properties need to be preserved and the extra subtype functionality shouldn't violate supertype properties.
If these three things are taken care of , you have abstracted away from the underlying stuff and you are writing loosely coupled code.
Source: Program Development in Java - Barbara Liskov