Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Liskov Substition and Composition

Let say I have a class like this:

public sealed class Foo
{
    public void Bar
    {
       // Do Bar Stuff
    }
}

And I want to extend it to add something beyond what an extension method could do....My only option is composition:

public class SuperFoo
{
    private Foo _internalFoo;

    public SuperFoo()
    {
        _internalFoo = new Foo();        
    }

    public void Bar()
    {
        _internalFoo.Bar();
    }

    public void Baz()
    {
        // Do Baz Stuff
    }
}

While this works, it is a lot of work...however I still run into a problem:

  public void AcceptsAFoo(Foo a)

I can pass in a Foo here, but not a super Foo, because C# has no idea that SuperFoo truly does qualify in the Liskov Substitution sense...This means that my extended class via composition is of very limited use.

So, the only way to fix it is to hope that the original API designers left an interface laying around:

public interface IFoo
{
     public Bar();
}

public sealed class Foo : IFoo
{
     // etc
}

Now, I can implement IFoo on SuperFoo (Which since SuperFoo already implements Foo, is just a matter of changing the signature).

public class SuperFoo : IFoo

And in the perfect world, the methods that consume Foo would consume IFoo's:

public void AcceptsAFoo(IFoo a)

Now, C# understands the relationship between SuperFoo and Foo due to the common interface and all is well.

The big problem is that .NET seals lots of classes that would occasionally be nice to extend, and they don't usually implement a common interface, so API methods that take a Foo would not accept a SuperFoo and you can't add an overload.

So, for all the composition fans out there....How do you get around this limitation?

The only thing I can think of is to expose the internal Foo publicly, so that you can pass it on occasion, but that seems messy.

like image 271
FlySwat Avatar asked Feb 16 '09 18:02

FlySwat


People also ask

What is liskov substitution method?

Simply put, the Liskov Substitution Principle (LSP) states that objects of a superclass should be replaceable with objects of its subclasses without breaking the application. In other words, what we want is to have the objects of our subclasses behaving the same way as the objects of our superclass.

What is the most accurate example of liskov substitution?

A good example here is that of a bird and a penguin; I will call this dove-penguin problem. The below is a Java code snippet showing an example that violates the LSP principle. Here, the Dove can fly because it is a Bird. In this inheritance, much as technically a penguin is a bird, penguins do not fly.

What violates LSP?

If substituting a superclass object with a subclass object changes the program behavior in unexpected ways, the LSP is violated. The LSP is applicable when there's a supertype-subtype inheritance relationship by either extending a class or implementing an interface.

Why it is called Liskov Substitution Principle?

The Liskov substitution principle (LSP) is a particular definition of a subtyping relation, called strong behavioral subtyping, that was initially introduced by Barbara Liskov in a 1988 conference keynote address titled Data abstraction and hierarchy.


2 Answers

I found myself asking that same question until I started working on reusable libraries of my own. Many times you wind up with certain classes that just cannot be extended without requiring obscure or arcane sequences of calls from the implementor.

When allowing your class to be extended, you have to ask: if a developer extends my class, and passes this new class to my library, can I transparently work with this new class? Can I work properly with this new class? Is this new class really going to behave the same?

I've found that most of the time the sealed classes in the .Net Framework have certain under-the-hood requirements that you aren't aware of, and that given the current implementation cannot be safely exposed to subclasses.

This doesn't exactly answer your question, but it provides insight as to why not all classes are inheritable in the .Net Framework (and why you should probably entertain sealing some of your classes too).

like image 69
user7116 Avatar answered Sep 21 '22 20:09

user7116


I'm afraid the short answer is, you can't without doing what is required, i.e. pass the composed instance variable instead.

You could allow an implicit or explicit cast to that type (whose implementation simply passed the composed instance) but this would, IMO be pretty evil.

sixlettervariable's answer is good and I won't rehash it but if you indicated which classes you wished you could extend we might be able to tell you why they prevented it.

like image 30
ShuggyCoUk Avatar answered Sep 21 '22 20:09

ShuggyCoUk