0

In C# - but it may be applicable to other languages - in the context of LSP, how can I ensure that a class inheriting from another (mutable) one will not break the original contract?

For example: If I have public, internal or protected property setters, there is a risk that inheriting classes will break the original contract. The same is applicable for virtual methods.

This is especially true if the owner of the parent and inheriting classes are not the same persons as it may introduce a lack of knowledge in the contract and in the intention of the original developer.

Is immutability the only one solution or are there some other ways of doing? I tend to consider that inheritance corresponds to a "behaves like a"-relationship instead of a "is a"-relationship. Is it a correct logical safeguard?

Here's an example for the sake of illustration:

public class Foo
{
    public virtual void DummyMethod(int dummyParameter)
    {
        if (dummyParameter > 10) { throw new ArgumentOutOfRangeException(); }
    }
}

public class Bar : Foo
{
    public override void DummyMethod(int dummyParameter)
    {
        if (dummyParameter < 0) { throw new InvalidOperationException(); }
    }
}

I noticed some other questions dealing with this topic (here for example) but I'm looking for general solutions or good practices to apply to avoid facing these issues upfront.

Community
  • 1
  • 1
Guillaume
  • 1,782
  • 1
  • 25
  • 42
  • 1
    Could you expand on what you mean by "behaves like a"? On a related note, how would you expect to stop classes from violating a contract when implementing an interface? (The same kind of answers quite possibly apply to this case...) – Jon Skeet Sep 09 '12 at 12:48
  • For example a square _is a_ rectangle but _does not behave_ the same way (as per the link to the article in my question). – Guillaume Sep 09 '12 at 12:51
  • For the interface implementation, I don't know. Actually I didn't really think of this as the interface does not have any contract beyond its own signature - at least in C# - so it's enforced by the compiler. In a class, I may be throwing an exception in one of the method for example but nothing indicates it from outside. – Guillaume Sep 09 '12 at 13:01
  • How can a derived class break the contract? The public and internal methods don't change (they still exist with the same signature). And behavior is supposed to change with each derived class; that's polymorphism. What are you looking to prevent? – Bob Horn Sep 09 '12 at 13:40
  • 1
    There is only one good way: use the *sealed* keyword. – Hans Passant Sep 09 '12 at 14:16
  • @BobHorn see the example I just added. The exception thrown in the subtype is not a subtype of `ArgumentOutOfRangeException`. If I don't own the parent class and the exception in the parent class is not documented, do I have any way to avoid such breach in the contract? – Guillaume Sep 09 '12 at 14:49
  • @HansPassant So you mean as soon as inheritance is allowed, and the behaviour of a class can be extended, we don't have any way to avoid violation of LSP? – Guillaume Sep 09 '12 at 14:51
  • @Guillaume This isn't a violation of LSP. The subtype can still be used in place of the base type. Yes, the behavior is changing, but that's okay. That's polymorphism. – Bob Horn Sep 09 '12 at 14:56
  • I don't know Java, but I believe you can specify the types of exceptions that will be thrown within a method. C# doesn't have that. – Bob Horn Sep 09 '12 at 15:04

1 Answers1

1

You can prevent violation of the LSP by using the sealed keyword.

This means you now can't inherit from this class at all, so you have to decide which you prefer: eliminating the risk of violating the LSP or the freedom to inherit and modify.

Credit to @Hans Passant for first mentioning sealed in comments.

Ed Guiness
  • 34,602
  • 16
  • 110
  • 145