IOC Conundrum

by Charles Miller on August 8, 2004

Using Spring, I've encountered an interesting architectural conundrum.

Say, just as a random example, that your application is centered around the Content class, which is the superclass of all, well, content. One of the obvious things you want to do here is that you want to send an 'asHTML' message to your content object and get the HTML back.

However, the rendering of a Content's content as HTML is a pretty complicated process, involving a somewhat nightmare-ish parser and runtime-loadable macros. So the renderer is an entirely separate component. This would mean that the Content class would have to delegate the rendering to the Renderer component, still no big deal.

Enter IOC/Dependency Injection. Now, each individual instance of the Content class needs to be injected with the renderer component by the IOC framework before any rendering can be performed. This is still possible: we're using Hibernate, so one solution would be to use Hibernate's lifecycle interfaces to ensure each Content object is auto-wired with its dependent components from the Spring context when the entity is loaded.

But what if you're pulling a few hundred or a thousand pieces of Content out of the database? A few wasted milliseconds resolving the object's dependencies becomes a few wasted seconds for a thousand objects1. Which is bad. Especially when the more of these content objects you're loading, the less likely it is that you're going to be rendering them as HTML in the first place.

At this point, you say screw it. Anyone who wants to render Content as HTML (despite the fact that being rendered as HTML is the raison d'etre of Content) can grab the Renderer themselves from Spring and pass the Content into it.

The price of this design decision is increased complexity. 3/4 of the system becomes dependent on the Renderer component. Ditto for the Security component, since for similar reasons you can't directly ask the Content object who is permitted to view it. And so on. Because you can't use your entity objects to do an object's primary function -- hiding complex implementation -- your system becomes a tangled web of component interdependencies.

To my mind, that makes the system brittle. Every change to a component has much more wide-reaching effects, and you end up duplicating a lot of code keeping the entities, and the component-derived metadata related to the entities in sync.

On the other hand, maybe this isn't the problem I think it is, and the alternative, complicating the entity objects by overloading them with large numbers of otherwise component-ised capabilities, is a cure worse than the disease.

I can't help thinking, though, that if I can come up with a clean solution to the problem, I might just be able to commit some significant acts of elegance on the codebase.

(Note: If you want to comment on this post, please try to do it in such a way that contributes to the sum of human knowledge. The answer isn't "Hibernate sucks", or "Spring sucks", or "IOC is a crutch for stupid people". Similarly, it doesn't matter if this is something that could almost certainly be solved with mix-ins in Ruby, because there are several good reasons I'm not programming in Ruby.)

Addendum: August 10th

I'm getting the feeling I misstated the problem. Or maybe I shouldn't have used that particular concrete example. People seem to be running away with the "What if I wanted to render the content as ASCII art?" question, which wasn't my intent at all.

The problem I was attempting to express was that with IoC, it's hard to give complex behaviour to an entity object. The entity can't (or at least shouldn't) discover system components for itself, and injecting (possibly unnecessary) dependencies into every single entity as it's retrieved from the ORM layer may be inefficient.

The result is a complex web of inter-component dependencies, because instead of saying "I can do this, this and this to a page", you instead have to say "I can do this to a page with the SecurityManager, this to a page with the LockManager, this to a page with the WikiStyleRenderer" and so on.

And yeah, generic method dispatch would probably work too, but I'm not using CLOS, nor am I likely to ever do so professionally.

1 I'm yet to time this, it's just a figure I pulled out of my asshat.

Previously: There Is No Justice

Next: The Principle of Charity