23

I am having trouble with the concept of interfaces interacting with polymorphic types (or even polymorphic interfaces). I'm developing in C# and would appreciate answers staying close to this definition, although i think that still gives plenty of room for everyone to put forth an answer.

Just as an example, let's say you want to make a program to paint things. You define an interface for the actor that Paints, and you define an interface for the subject which is painted, Furthermore you have some subjects which can be painted in a more specific way.

interface IPainter {
  void paint(IPaintable paintable);
}
interface IPaintable {
  void ApplyBaseLayer(Color c);
}
interface IDecalPaintable : IPaintable {
  void ApplyDecal(HatchBrush b);
}

I can imagine making a painter similar to the following:

class AwesomeDecalPainter : IPainter
  {
    public void paint(IPaintable paintable) {
      IDecalPaintable decalPaintable = (IDecalPaintable)paintable;
      decalPaintable.ApplyBaseLayer(Color.Red);
      decalPaintable.ApplyDecal(new HatchBrush(HatchStyle.Plaid, Color.Green));
    }
  }

Of course this will throw if paintable does not implement IDecalPaintable. It immediately introduces a coupling between the IPainter implementation and the IPaintable that it operates on. However I also don't think it makes sense to say that AwesomeDecalPainter is not an IPainter just because it's use is limited to a subset of the IPaintable domain.

So my question is really four-fold:

  • Are interface compatible with polymorphism at all?
  • Is it good design to implement an IPainter that can operate on IDecalPaintable?
  • What about if it can exclusively operate on IDecalPaintable?
  • Is there any literature or source code that exemplifies how interfaces and polymorphic types should interact?
hannasm
  • 1,951
  • 4
  • 16
  • 25
  • 3
    For the reasons why `IDecalPainter.paint` is a bad design, see ["What is the Liskov Substitution Principle?"](http://stackoverflow.com/questions/56860/what-is-the-liskov-substitution-principle/56904#56904). What you should have is an overloaded `paint` that takes an `IDecalPaintable` so that `AwesomeDecalPainter` can accept both `IPaintable`s and `IDecalPaintable`s. – outis May 27 '11 at 04:23
  • 1
    ... An `IPainter` should be able to `paint` on any `IPaintable`. It makes perfect sense to say that an `AwesomeDecalPainter` is not an `IPainter` because it can't operate on all `IPaintable`s. See the [circle-ellipse problem](http://en.wikipedia.org/wiki/Circle-ellipse_problem), also known as the [square-rectangle problem](http://cafe.elharo.com/programming/a-square-is-not-a-rectangle/) – outis May 27 '11 at 04:23
  • It doesn't seem possible to say an IPainter should be able to paint on *some* IPaintable, yet the language directly facilitates that usage. Perhaps an interface can safely operate on a polymorphic type, but interfaces cannot themselves be polymorphic. – hannasm May 27 '11 at 05:17
  • So an IPainter isn't allowed to paint on just *some* IPaintables. And inheriting from IPainter means you have no choice but to always be compatible with *exactly* the IPaintable interface. So what exactly could you ever add in a subinterface of IPainter. – hannasm May 27 '11 at 05:30
  • 1
    You can add anything you want as long as you support IPaintable. What your interface is saying is that anyone who is an IPainter can also operate on any substrate that is an IPaintable to ApplyBaseLayer. That means I can have anyone from a kid from the ghetto to Rembrandt's apprentice show up, as long as they are an IPainter, and I can give them anything from a canvas to a brick wall, as long as it is an IPaintable, and they can ApplyBaseColor. Period. What else they can do is whatever else they can do. It is not defined by IPainter and IPaintable and as a client I don't know or care. – Sisyphus May 27 '11 at 06:03
  • 1
    @SmokingRope: interfaces aren't polymorphic because they don't have any methods; they only define method signatures. They *support* polymorphism because different classes implementing the same interface can have their own method implementations, yet all have the same methods with the same signatures. Defining a `paint` method with an `IPaintable` argument that only works on `IDecalPaintable` breaks not only the Liskov substitution principle, it breaks the type system. – outis May 27 '11 at 06:11
  • Yea i think that's the conclusion i've come to as well, interfaces aren't themselves polymorphic. And infact wikipedia defines polymorphism in terms of interfaces, so trying to define a polymorphic interface would be self-referrential and possibly cause a stack overflow! – hannasm May 27 '11 at 06:30
  • Interfaces inheriting interfaces is not actually inheritance but composition. – hannasm May 27 '11 at 06:31

4 Answers4

19

The interface of a class is meant as a tool for the "user" of that class. An interface is a public presentation for the class, and it should advertise, to anyone considering to use it, what methods and constants are available and accessible from the outside. So, as it name suggests, it always sits between the user and the class implementing it.

On the other hand, an abstract class is a tool aimed at helping the "implementor" of the classes that extend it. It is an infrastructure that can impose restrictions and guidelines about what the concrete classes should look like. From a class design perspective, abstract classes are more architecturally important than interfaces. In this case, the implementor sits between the abstract class and the concrete one, building the latter on top of the former.

So to answer your question simply, Interface is a "contract" for the code to respect. When uused this way, it applies more to inheritance that polymorphism.

Abstract classes, they define a 'type'. And when the concrete sub classes use abstract classes and redefine methods, add new ones etc ... there you see polymorphism in action.

I know this post may confuse you more, it didn't make sense to me until I learned design patterns. With a few simple patterns under your belt you will better understand the role of each object and how inheritance, polymorphism and encapsulation play hand in hand to build cleanly designed applications.

Good-Luck

Stephane Gosselin
  • 9,030
  • 5
  • 42
  • 65
  • 1
    Agreed! I took on the book Head First design patterns and felt like his was everything they left out of the undergraduate program. Without it it's just a bunch of tools for your toolbox but you dont quite have the duct tape to put them all together yet. – Mohgeroth May 27 '11 at 04:49
9

Interfaces are not only compatible with polymorphism--they're essential to it. Part of the idea you seem to be missing is that if one has an interface like IPaintable, the expectation is that every object that implements it will provide some default method of being painted, typically encapsulating other methods which would configure the object in some useful fashion.

For example, an IPaintable interface might define a paint method:

void Paint(ICanvas canvas);

Note that the Paint method says nothing about what should be painted, nor what color, nor anything else. Various paintable objects would expose properties to control such things. A Polygon method, for example, might dispose OutlineColor and FillColor properties, along with a SetVertices() method. A routine that wants to paint a whole lot of objects could accept an IEnumerable(Of IPaintable) and simply call Paint on all of them, without having to know anything about how they'll be painted. The fact that an object is painted by calling Paint with no parameters other than the canvas upon which it should be painted in no way prevents the Paint method from doing all sorts of fancy gradient fills, texture mapping, ray tracing, or other graphical effects. The code which is commanding the painting would know nothing of such things, but the IPaintable objects themselves would hold all the information they needed and thus wouldn't need the calling code to supply it.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • This is the best answer of the bunch. – Zak Jun 10 '11 at 03:58
  • @Zak: Glad you like it. The idea of an application being able to paint something without having any idea what that actually entails goes back at least to the Macintosh; I remember running an old version of TeachText (like Notepad, but with the ability to show pictures that are embedded in a document's resource fork using some other program), which was written before color Macintoshes existed, on a Macintosh II, and being rather surprised when I opened up a document that contained a color picture and it showed up in color. It turns out the Macintosh picture support... – supercat Jun 10 '11 at 14:40
  • ...wasn't nearly as universal as that first experience made me think (old applications would be limited to eight colors) but I think the principle is applicable here. The fact that IPaintable only exposes a single boring Paint method doesn't in any way restrict the ability of the Paint method to do things far beyond anything an application using IPaintable might anticipate. – supercat Jun 10 '11 at 14:42
5

The problem is really that you have defined a vague interface, a contract is a more proper term. It's like you go into McDonalds and just order a burger:

interface Resturant
{
    Burger BuyBurger();
}

The person taking your order will look a bit confused for a while, but eventually he/she will serve you any burger since you don't specify what you want.

Same thing here. Do you really just want to define that something is paintable? If you ask me, it's like asking for trouble. Always try to make interfaces as specific as possible. It's always better to have several small specific interfaces than a large general one.

But let's go back to your example. In your case, all classes only have to be able to paint something. Therefore I would add another more specific interface:

    interface IPainter
    {
        void Paint(IPaintable paintable);
    }
    interface IDecalPainter : IPainter
    {
        void Paint(IDecalPaintable paintable);
    }
    interface IPaintable
    {
        void ApplyBaseLayer(Color c);
    }
    interface IDecalPaintable : IPaintable
    {
        void ApplyDecal(HatchBrush b);
    }

    class AwesomeDecalPainter : IDecalPainter
    {
        public void Paint(IPaintable paintable)
        {
            IDecalPaintable decalPaintable = paintable as IDecalPaintable;
            if (decalPaintable != null)
                Paint(decalPaintable);
            else
                paintable.ApplyBaseLayer(Color.Red);
        }

        public void Paint(IDecalPaintable paintable)
        {
            paintable.ApplyBaseLayer(Color.Red);
            paintable.ApplyDecal(new HatchBrush(HatchStyle.Plaid, Color.Green));
        }
    }

I would recommend that you read about the SOLID principles.

Update

A more sound interface implementation

    interface IPaintingContext
    {
        //Should not be object. But my System.Drawing knowledge is limited
        object DrawingSurface { get; set; }
    }
    interface IPaintable
    {
        void Draw(IPaintingContext context);
    }

    class AwesomeDecal : IPaintable
    {
        public void Draw(IPaintingContext paintable)
        {
            // draw decal
        }
    }

    class ComplexPaintableObject : IPaintable
    {
        public ComplexPaintableObject(IEnumerable<IPaintable> paintable)
        {
            // add background, border 
        }
    }

Here you can create as complex painting items as possible. They are now aware what they paint on, or what other paintables are used on the same surface.

jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • You are suggesting that AwesomeDecalPainter degrade itself to painting things in a less awesome way? Seems to me that it's violating its purpose. – hannasm May 27 '11 at 05:09
  • No, seems like it's behaving exactly as defined by the interface. The problem is not the degrading but the defined interface. – jgauffin May 27 '11 at 05:19
  • I've added an alternative implementation making better use of interfaces. The Context interface could have some general painting methods instead, making it possible to paint on anything. – jgauffin May 27 '11 at 05:27
  • In this second revision, the IPaintable interfaces are still limited to painting in exactly the manner allowed by IPaintingContext. There's no way for IPaintingContext to have a derived IAwesomePaintingContext that would be supported by the other interface. Thus, interfaces are incompatible with polymorphism? – hannasm May 27 '11 at 05:50
  • No. Not true. The thing with OOP is that you MUST implement classes/interfaces as their contracts describe. Hence if `IAwesomePaintingContext` inherits `IPaintingContext` it should work as described by `IPaintingContext` if an object can only use `IPaintingContext` but not `IAwesomePaintingContext`. If a class cannot work with `IPaintingContext` it should not accept it but only a `IAwesomePaintingContext`. If you can't make that work, you need to refactor your architecture. – jgauffin May 27 '11 at 06:28
  • Inheritance is about extension (you ADD features), not about modification (you may not CHANGE existing features). Thus the degradation in the first example is what is expected and anything else would be undefined behavior. – jgauffin May 27 '11 at 06:37
  • Would it be a violation if AwesomeDecalPainter does not paint a decal? Would it be a violation of IPainter if it does not paint anything? In some scenarios it might even be included in the contract that an implementor can throw if the input is invalid. (Think IConvertible in .NET framework) In that case IPainter / IPaintable is just fine as originally written. – hannasm May 27 '11 at 07:08
  • Not painting a decal is indeed a violation if the contract says that a decal should always be painted. Since then it's *expected* that a decal is painted. All I'm saying is that you want to follow strict OOP principles you have to do what the contract says. In the real world such design can lead to unnecessary complex systems just as you do not always follow the three normal forms (3NF) when designing a database. `IConvertable` is an example of something that can fail terribly due to it's design if it's not used as intended but as the contract it defines. – jgauffin May 27 '11 at 07:32
0

Am I understanding your question correctly?

I think your question is whether interface can exercise the essentials of inheritance ?

(ie. child interface inheriting from parent interface).

1) if that's the case, syntactically sure, why not?

2) as for the practicability, it may be of little value because, your child interfaces are not essentially reusing anything from your parent interface.

Cid
  • 14,968
  • 4
  • 30
  • 45
Gary Tsui
  • 1,755
  • 14
  • 18
  • My question is really broad, can an interface 'contract' specify polymorphic behavior. Can the interfaces specification exhibit polymorphic behavior. It's clearly possible, but is it a good idea? I think a lot of the comments in this thread have helped shed some light however. – hannasm May 27 '11 at 07:18
  • #SmokingRobe, i might have turned your question into another one, which is based on my understanding to your question, and ive documented it. http://gray-suit.blogspot.com/2011/05/interface-inheriting-from-another.html As to answering whether this is a good idea, i want to ask one first. Why proceed with Interface and not just Sup/sub-classing? If you are already inheriting from one place, and can't 'directly' inherit from another. Then, like the posts above stated, you may need to refactor abit. – Gary Tsui May 27 '11 at 08:39
  • Well the primary advantage of the interface in C# is multiple ineritance. More generally the interface means two implementors can be cast to the same interface without the implementations being related in a formal inheritance hierarchy. Unrelated types could have support for a common behavior without following the is-a relationship. – hannasm May 27 '11 at 15:58