Friday, March 17, 2006

Faux Collection Class Subclassing

Though message forwarding is fun, I disagree with most of AgentM’s take on Cocoa’s class clusters.

First, I don’t think that subclassing NSArray gives you “essentially zero functionality to start with”; rather, it gives you all of the derived methods, and you just have to implement the primitive ones. (The same is true if you subclass NSMutableArray.) Why use NSProxy to forward the derived methods to a regular array instance variable, when the abstract array class is already designed to do that for you?

Second, I don’t think that the examples given—a “message in a server-client protocol,” a queue, and mutable array that constrains the type of its elements, etc.—are good candidates for subclasses. The message example sounds more like has-a than is-a. And the same for a queue—why inherit all those methods only to make all but push and pop illegal (without much help from the compiler)? Lastly, a type-checking array would violate the substitution principle, so you couldn’t reliably pass it to an API that expects a regular mutable array. One tip-off that this is a troublesome design is that equality between the typed array and the stock one won’t be symmetric. It’s kind of the same situation as NSString and NSAttributedString. If you think about it, there isn’t really a way to make that subclassing relation work—which is why NSAttributedString is a decorator, rather than a subclass.

AgentM contends that class clusters break “the basic object-oriented principle of inheritance,” since you never know which array subclass you’re going to get. My take is that the basic object-oriented principle is that messages—and what they promise to do—are what matter, not classes. Inheritance is useful to the extent that proper subclasses respond to a common set of messages and agree on what they mean. But it should be thought of primarily as a tool for design, rather than a mechanism of code reuse. If you find yourself wanting to subclass an array or string and “add simple methods and instance variables particular to [your] situation,” I have to question whether that makes sense at a design level. Rather than a new kind of array, you probably want an object that contains an array of something.

An example of where subclassing makes sense is making a class that’s functionally identical to a standard array, but that has different implementation characteristics. For example, you might join two large immutable arrays using a subclass that doesn’t bother copying all the elements into new storage. Or implement a subarray using just a reference and a range. Or your subclass could use a database or a C array of primitive types as an optimized backing store. Not coincidentally, Apple’s class clusters make it easy to do this kind of subclassing.

The Cocoa frameworks are very well designed. There are a few warts—such as making file references NSStrings rather than a distinct type—but I don’t think that class clusters are one of them.

1 Comment RSS · Twitter

Jens Alfke reminds us that NSInvocation is comparatively slow. However, like him I use it for inter-thread messaging, as well as to delay messages.

Leave a Comment