Back when I started reading about Object-Oriented Programming (which was when Java was new, I was using Delphi and maybe the ArcGIS scripting language, which also had OO features) the entire hotness was inheritance. Class hierarchies as complicated as biological taxonomies were diagrammed, implemented and justified. Authors explained that while both a Dog and a Cat could makeNoise(), they’d do it in different ways. They also got their knickers in a knot over whether a Square is-a Rectangle or the other way around. If you believe in successive specialisation then the Square is-a (special kind of) Rectangle. If you believe in the Liskov Substitution Principle then it cannot be. If you don’t care then you’d only define a Rectangle in the first place.
Slightly earlier than this time it was noticed that you sometimes want to separate the idea of “this object does these things” from “this object is something of this type”. C++ programmers were inheriting from purely virtual classes. Objective-C gave us protocols to conform to; Java borrowed this idea and called the protocols interfaces.
Both inheritance and conformance (which is what I’ll call protocol adoption, to avoid the confusion between Objective-C and Java over what it means to “implement” an “interface”) are blunt and unwieldy instruments, however. In practice, I use them in a few specific places:
- I most often inherit from a “base class”. My Python object is-an object, my Java object is-an (java.lang.)object, my ObjC object is-a NSObject.
- I sometimes inherit from deeper classes because something requires me to build a specialised version of that class. For example, UIKit requires that my view controllers are specialised versions of UIViewController, and that my views are specialised versions of those views.
- I usually adopt a protocol or abstract interface in order to implement some design pattern. For example I might adopt a data source or delegate protocol. I might provide an implementation of a strategy or state or visitor.
- I pretty much avoid even with bargepoles the “marker interfaces” that pervade pre-annotation Java, like Cloneable. I even hate NSCopying as a way of designing a protocol.
It’s time we added specialised syntax on top of inheritance to account for these cases particularly. For a start, any language that doesn’t assume you want its usual version of Object-ness when you create a class without saying anything about it is broken. Yes, I’m looking at you Objective-C. Yes, I know about NSProxy and I’m old enough to remember Object. I still say that not wanting an NSObject is rare enough that you should have to shout about it. I’ve never intentionally created a new Objective-C root class – but that’s the easiest thing the syntax permits.
Think of it like pointers. Yes, you can use pointers in ObjC and C++. Yes, arrays are built using pointers—as are object references. No you don’t need to use pointer syntax to build arrays and object references – we’ve got higher-level syntax. Just like we’ve got higher-level syntax for getting at properties or at subscripted entries in Objective-C.
So yes, you can create a delegate using protocols. But why not have syntax saying “this class’s delegates must do this” and “this is a delegate of that class”? Why not say “this is a special version of that thing”? Why not say “this group of classes implement a state machine?”
Inheritance is old and busted.