What is an example of the Liskov Substitution Prin

2018-12-31 00:48发布

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?

26条回答
宁负流年不负卿
2楼-- · 2018-12-31 01:32

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:

  • In-variants of base class must be preserved by the derived class
  • Pre-conditions of the base class must not be strengthened by the derived class
  • Post-conditions of the base class must not be weakened by the derived class.

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

查看更多
骚的不知所云
3楼-- · 2018-12-31 01:33

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 from BTest. 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?"

查看更多
墨雨无痕
4楼-- · 2018-12-31 01:34

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 a Rectangle. Indeed it is a specialization of a rectangle. The "is a" makes you want to model this with inheritance. However if in code you made Square derive from Rectangle, then a Square should be usable anywhere you expect a Rectangle. This makes for some strange behavior.

Imagine you had SetWidth and SetHeight methods on your Rectangle base class; this seems perfectly logical. However if your Rectangle reference pointed to a Square, then SetWidth and SetHeight doesn't make sense because setting one would change the other to match it. In this case Square fails the Liskov Substitution Test with Rectangle and the abstraction of having Square inherit from Rectangle is a bad one.

Y'all should check out the other priceless SOLID Principles Motivational Posters.

查看更多
墨雨无痕
5楼-- · 2018-12-31 01:34

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.

查看更多
柔情千种
6楼-- · 2018-12-31 01:35

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 :

<?php

interface Database 
{
    public function selectQuery(string $sql): array;
}

class SQLiteDatabase implements Database
{
    public function selectQuery(string $sql): array
    {
        // sqlite specific code

        return $result;
    }
}

class MySQLDatabase implements Database
{
    public function selectQuery(string $sql): array
    {
        // mysql specific code

        return $result; 
    }
}

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 :

<?php

interface Database 
{
    public function selectQuery(string $sql): array;
}

class SQLiteDatabase implements Database
{
    public function selectQuery(string $sql): array
    {
        // sqlite specific code

        return $result;
    }
}

class MySQLDatabase implements Database
{
    public function selectQuery(string $sql): array
    {
        // mysql specific code

        return ['result' => $result]; // This violates LSP !
    }
}

Now the subtypes cannot be used the same way since they don't produce the same result anymore.

查看更多
无与为乐者.
7楼-- · 2018-12-31 01:37

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:

  1. 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.

  2. Methods Rule: The implementation of those operations is semantically sound.

    • Weaker Preconditions : The subtype functions should take at least what the supertype took as input, if not more.
    • Stronger Postconditions: They should produce a subset of the output the supertype methods produced.
  3. Properties Rule : This goes beyond individual function calls.

    • Invariants : Things that are always true must remain true. Eg. a Set's size is never negative.
    • Evolutionary Properties : Usually something to do with immutability or the kind of states the object can be in. Or maybe the object only grows and never shrinks so the subtype methods shouldn't make it.

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

查看更多
登录 后发表回答