Quantcast
Viewing all articles
Browse latest Browse all 6

Wacky Ideas 1: Inheritance is dead, long live mix-ins!

(Warning: I’ve just looked up “mix-in” on Wikipedia and their definition isn’t quite what I’m used to. Apologies if I’m using the wrong terminology. What I think of as a mix-in is a proxy object which is used to do a lot of the work the class doing the mixing says it does, but preferably with language/platform support.)

I’ve blogged before about my mixed feelings about inheritance. It’s very useful at times, but the penalty is usually very high, and if you’re going to write a class to be derived from, you need to think (and document) about an awful lot of things. So, how about this: we kill of inheritance, but make mix-ins really easy to write. Oh, and I’ll assume good support for closures as well, as a lot can be done with the Strategy Pattern via closures which would otherwise often be done with inheritance.

So, let’s make up some syntax, and start off with an example from the newsgroups. The poster wanted to derive from Dictionary<K,V> and override the Add method to do something else as well as the normal behaviour. Unfortunately, the Add method isn’t virtual. One poster suggested hiding the Add method with a new one – a solution I don’t like, because it’s so easy for someone to break encapsulation by using an instance as a plain Dictionary<K,V>. I suggested re-implementing IDictionary<K,V>, having a private instance of Dictionary<K,V> and making each method just call the corresponding one on that, doing extra work where necessary.

Unfortunately, that’s a bit ugly, and for interfaces with lots of methods it can get terribly tedious. Instead, suppose we could do this:

using System.Collections.Generic;

class FunkyDictionary<K,V> : IDictionary<K,V>
{
IDictionary<K,V> proxyDictionary proxies IDictionary<K,V>;

void IDictionary<K,V>.Add(K key, V value)
{
// Do some other work here

proxyDictionary.Add(key, value);

// And possibly some other work here too
}
}

Now, that’s a bit simpler. To be honest, that kind of thing would cover most of what I use inheritance for. (Memo to self: write a tool which actually finds out how often I do use inheritance, and where, rather than relying on memory and gut feelings.) The equivalent of having an abstract base class and overriding a single method would be fine, with a bit of care. The abstract class could still exist and claim to implement the interface – you just implement the “missing” method in the class which proxies all the rest of the calls.

The reason it’s important to have closures (or at least delegates with strong language support) is that sometimes you want a base class to be able to very deliberately call into the derived class, just for a few things. For those situations, delegates can be provided. It achieves the same kind of specialization as inheritance, but it makes it much clearer (in both the base class and the “derived” one) where the interactions are.

One point of interest is that without any inheritance, we lose the benefits of a single inheritance tree – unless object becomes a general “any reference”, which is mostly what it’s used for. Of course, there are a few methods on System.Object itself which we’d lose. Let’s look at them. (Java equivalents aren’t specified, but Java-only ones are):

  • ToString: Not often terribly useful unless it’s been overridden anyway
  • GetHashCode/Equals: Over time I’ve been considering that it may have been a mistake to make these generally available anyway; when they’re not overridden they tend to behave very differently to when they are. Wrapping the existing behaviour wouldn’t be too hard when wanted, but otherwise make people use IEquatable<T> or the like
  • GetType: This is trickier. It’s clearly a pretty fundamental kind of call which the CLR will have to deal with itself – would making it a static (natively implemented) method which took an object argument be much worse?
  • MemberwiseClone: This feels “systemy” in the same way as GetType. Could something be done such that you could only pass in “this“? Not a terribly easy one, unless I’m missing something.
  • finalize (Java): This could easily be handled in a different manner, similar to how .NET does.
  • wait/notify/notifyAll (Java): These should never have been methods on java.lang.Object in the first place. .NET is a bit better with the static methods on the Monitor class, but we should have specific classes to use for synchronization. Anyway, that’s a matter I’ve ranted about elsewhere.

 

What are the performance penalties of all of this? No idea. Because we’d be using interfaces instead of concrete classes a lot of the time, there’d still be member lookup even if there aren’t any virtual methods within the classes themselves. Somehow I don’t think that performance will be the reason this idea is viewed as a non-starter!

Of course, all of this mix-in business relies on having an interface for everything you want to use polymorphically. That can be a bit of a pain, and it’s the subject of the next article in this series.


Viewing all articles
Browse latest Browse all 6

Trending Articles