Where I build a composition-based object inheritance mechanism in Objective-C.
They say inheritance is bad. Yes, there really are 5 different links in the previous sentence.
The links I’ve provided may not be the most trustworthy sources on the subject (as the matter of fact, I’ve entered ‘inheritance is bad’ in the Google prompt and used the top results just to provide a couple of examples). Jokes aside, there is much info out there about the reasons to prefer composition over inheritance in most cases.
As with all of such statements you do not blindly follow the rule. Inheritance is a tool, and as a software engineer, you should know when and where to use your tools.
I’m using composition for building complex objects a lot these days. One of the patterns I’ve been using lately is adding Objective-C protocol conformance to specific objects (i.e. not classes, but individual instances) with the help of wrapper (or decorator) objects. To simplify the interaction with these wrappers I am using the message forwarding mechanism, which allows me treating the wrapper in the same way as the original object, but with the benefits of the added protocol.
Now starting with that in mind I’ve come up with an interesting little runtime construct, which utilizes composition and message forwarding to implement multiple inheritance-like behaviors on the instance level.
The idea is actually pretty simple. In the scheme described above my wrapper object implements the protocol, which it is designed to add to the wrapped object. However, abstracting the protocol implementation from the wrapper and implementing it in a separate class could achieve the same result. The wrapper itself would only be responsible for proper message forwarding between the objects it combines.
Now, why would we stop on protocols only? We could easily combine several classes just for the sake of doing it and say that our wrapper ‘inherits’ these objects’ properties and methods. If we override the reflection methods such as conformsToProtocol:
and isKindOfClass:
, our wrapper could pose as the objects which it wraps, implementing ‘is-a’ relationship almost flawlessly. This looks a lot like inheritance to me.
Suppose the interface of our proxy class would look like this:
The first method adds the parent object to the list of wrapped objects. Messages received by the proxy will be forwarded to one of these. However, there could be situations when several of the proxied objects do respond to a specified selector, and the proxy would not know which one to forward the message to. That is where the second method comes into the play. We could select a designated target for individual selectors to make sure that of several objects responding to the given selector, only the one which we want to will actually receive the message.
The implementation is pretty straightforward:
I’m aiming for the thread-safe implementation, hence the atomic properties and copying the dictionaries instead of mutating them directly. Parent objects are stored using the class name as a key; designated targets are stored using the selector name as a key. Note that the designated targets are entirely independent of the parent objects. This is intentional. If you wanted, you could construct the proxy by adding method implementations one by one using the designated targets API.
Now let’s look at the forwarding part. First we write a couple of helper methods to work with designated targets:
Then the actual forwarding mechanism is implemented like this:
We are almost there now. To make our proxy completely interchangeable with the objects it wraps, we may want to override isEqual:
too. We could try to provide a similar implementation for this method:
However, here be dragons, they say. Remember that overriding isEqual:
also requires having a consistent hash
implementation? If two objects are equal, they should also have equal hashes. Suppose we have several parent objects and are comparing the proxy with each of the parents using isEqual:
. In that case, this implementation will return YES
each time, but which value of the hash
would we return? Each parent could return a different hash in general.
That makes the creation of a universal isEqual:
implementation kind of impossible. A designated ‘identity’ object, which is also set externally and is used to forward both isEqual:
and hash
calls, is probably a decent tradeoff.
I’ve not actually come up with a scenario when this would be useful yet. As usual, the code is available on GitHub; feel free to have a look if you are interested. My lack of ideas for usage scenarios gets kind of clear when you look at the unit tests, which I’ve provided for the class. Without a proper usage scenario, these tests are rather artificial and contrived.
It is worth mentioning that this proxy class is rather useless by itself. You generally would not gain anything by just blindly combining several objects into one and providing automatic forwarding mechanisms. These objects are still completely independent. The usage scenario I am having in mind involves subclassing WWInheritanceProxy
and providing custom implementations of some of the methods, which will allow the resulting object to be more than just a sum of its parts. You supply the most important methods; automatic forwarding gives the rest.