質問

The LSP says "The derived types must not change the behavior of the base types", in other words "Derived types must be completely replaceable for their base types."

This means that if we define virtual methods in our base classes, we have violated this principle.

Also if we hide a method in the drive method by using new keyword then again we have violated this principle.

In other words, if we use polymorphism we have violated LSP!

In many applications I've used Virtual methods in the base classes and now I realize it violates LSP. Also if you use Template Method pattern you have violated this principle that I've used it a lot.

So, how to design your application that complies with this principle when you'd need inheritance and you'd like to benefit also from polymorphism? I'm confused!

See the example from here: http://www.oodesign.com/liskov-s-substitution-principle.html

役に立ちましたか?

解決 3

LSP says that you must be able to use a derived class in the same way you use it's superclass: "objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program". A classic inheritance that breaks that rule is deriving Square class from Rectangle class since the former must have Height = Width, while the latter can have Height != Width.

public class Rectangle
{
    public virtual Int32 Height { get; set; }
    public virtual Int32 Width { get; set; }
}

public class Square : Rectangle
{
    public override Int32 Height
    {
        get { return base.Height; }
        set { SetDimensions(value); }
    }

    public override Int32 Width
    {
        get { return base.Width; }
        set { SetDimensions(value); }
    }

    private void SetDimensions(Int32 value)
    {
        base.Height = value;
        base.Width = value;
    }
}

In this case, the behavior of Width and Height properties changed and this is a violation of that rule. Let's take the output to see WHY the behavior changed:

private static void Main()
{
    Rectangle rectangle = new Square();
    rectangle.Height = 2;
    rectangle.Width = 3;

    Console.WriteLine("{0} x {1}", rectangle.Width, rectangle.Height);
}

// Output: 3 x 2

他のヒント

Barbara Liskov has a very good article Data Abstraction and Hierarchy where she specifically touches polymorphic behavior and virtual software constructions. After reading this article you can see, that she describes in deep how software component can achieve flexibility and modularity from simple polymorphic calls.

LSP states about implementation details, not abstractions. Specifically, if you consume some interface or abstraction of type T, you should expect to pass all subtypes of T and not to observe unexpected behavior or program crash.

The keyword here is unexpected, because it can describe any of the properties of your program (correctness, task performed, returned semantics, temporarily and so on). So making you methods virtual does not mean by itself violating LSP

"The derived types must not change the behavior of the base types" means that it must be possible to use a derived type as if you were using the base type. For instance, if you are able to call x = baseObj.DoSomeThing(123) you also must be able to call x = derivedObj.DoSomeThing(123). The derived method should not throw an exception if the base method didn't. A code using the base class should be able to work well with the derived class as well. It should not "see" that it is using another type. This does not mean that the derived class has to do exactly the same thing; that would be pointless. In other words using a derived type should not break the code that was running smoothly using the base type.

As an example let's assume that you declared a logger enabling you to log a message to the console

logger.WriteLine("hello");

You could use constructor injection in a class needing to produce logs. Now instead of passing it the console logger you pass it a file logger derived from the console logger. If the file logger throws an exception saying "You must include a line number in the message string", this would break LSP. However, it is not a problem that the logging goes to a file instead of the console. I.e. if the logger shows the same behavior to the caller, everything is okay.


If you need to write a code like the following one, then LSP would be violated:

if (logger is FileLogger) {
    logger.Write("10 hello"); // FileLogger requires a line number

    // This throws an exception!
    logger.Write("hello");
} else {
    logger.Write("hello");
}

By the way: The new keyword does not influence polymorphism, instead it declares a completely new method that happens to have the same name as a method in the base type but is not related to it. In particular, it is not possible to call it through a base type. For polymorphism to work, you must use the override keyword and the method must be virtual (unless you are implementing an interface).

I think the Liskov's Substitution Principle (LSP) is mainly about moving the implementation of functions that may differ to the children classes and leave the parent class as general as possible.

So whatever you change in the child class, it does not break the Liskov's Substitution Principle (LSP) as long as this change does not force you to modify the code in the parent class.

Subtypes must be replaceable by base types.

In terms of contacts.

Derived class can replace base class pre-condition for the same or weaker and post-condition for the same or greater.

Link

For polymorphism to work, LSP must be being adhered to. A fine way to break it would be to introduce methods in a derived type that aren't in the base type. In that instance, polymorphism can't work because those methods are not available in the base type. You can have a different subtype implementation of a method, whilst adhering to both polymorphism and LSP.

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top