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.
After I wrote this I realised I've probably not added much other than to re-state what you'd already said but I decided to post it anyway.
To my mind, the problem of rendering is not the responsibility of the content. If it was, anytime you wanted to add another type of rendering, you'd end up modifying the content class as required.
Rendering is a "strategy" so you pass it into the content object and have the it use a push model ala "visitor" or have the renederer simply pull data from the content.
On question in my mind is why does the content even need to know about rendering? If you think of content as a context ala velocity.
Now, performing the rendering may well be something as simple as a single method call on an interface. Are there really that many different places that happens? And if so why?
Even if there is, is it really that bad especially given you have injection so all you need to do is declare that you need one.
My experience of using ctor based DI is that it has actually reduced the coupling between classes but does introduce more layers of "join" classes between classes leading to a larger numbers f smaller classes. I find this "complex arrangement of simple classes" to be much easier to understand, debug and develop. But that may be a very personal thing.
We have also implemented some custom hibernate types to handle lookup of data from external systems and our approach to DI is to trawl the meta-data at config-time and inject the services that way. Works just fine.
Cheers,
Simon
Do you really need to use IOC here ? or is it a case of everything looking like a nail, once you have a hammer ? It will help if you can put up some UML/block diagrams of the problem so that people can attack away at the problem.
This is an interesting problem, which we ran into as well. Basically, if a stateful domain object has service methods which in turn requires access to services and components, it becomes difficult to handle since the persistence mechanism has to be tied into the IoC stuff, and the performance will go down because of the massive number of dependency injections.
If you solve this the "regular" way you'd do what Simon tells you: "don't do it", which in practice means that you'd break all the service methods into a separate service, all of which would have as its first argument a reference to the object to work with, e.g. "Foo BarService.doServiceStuff(barObject,...)".
But what if you want your domain model to not only be a dumb graph of state? What if you really do want it to contain both state and logic? Well, we solved that by using AOP introductions. Basically, you split off the service methods much the same way you'd do if you wanted to implement them as a separate service, but skip the "barObject" parameter. Then that interface and corresponding implementation is introduced into the object class as a singleton. Instead of using the first parameter as the "context" you can then simply use the "this" pointer.
Since the implementation is introduced as a singleton the dependency injection is only done. This means that there's no problem with having lots of objects loaded, from a performance point of view. Also, the persistence mechanism does not have to be integrated with the IoC container.
All in all, this makes it possible to have a domain model which isn't stupid.
Using your example, if you have a content object, all you have to do is cast it to the service interface and use the method, like so: "String html = ((HtmlRenderable)content).toHtml();".
The nice thing with having logic and state in the object itself is that it becomes much easier to see what you can do with an object. Let's say you use the traditional "object/service" separation. If you then get a content object there's no good way to tell "what services can I use with this?". If you instead use AOP introductions all you have to do is "content.getClass().getInterfaces()" or "content instanceof HtmlRenderable" and you can immediately see what it can do.
(I'll be doing a talk on this topic at the JavaZone conference in Oslo this fall, see www.javazone.no for details)
Simon: You could be right, I might be barking up the wrong tree, and should just leave things that are comfortable being services to _be_ services. But if I didn't go on these mental excursions occasionally, I'd never know.
Ravi: I'm far too lazy to draw UML diagrams.
Rickard: yeah, that's pretty close to what I'd have done with Ruby mix-ins if I'd had that option. Shame I can't make it to Oslo, though, sounds like an interesting talk.
In your example, I'd say that the renderer is stateless in nature, thefore it can be static. You could use a single instance to "stamp" output for several Content instances, like JTable uses a single CellRenderer to render all its cells.
Maybe I'm missing your point. Can you give me an example which does not imply the mixture of presentation and logic?
As I was reading this blog entry, I'm thinking that this looks like the poster child example of Strategy and Vistor.
I read the first comment, and am thinking 'Amen to you'
And then I realise it's bloody Simon Harris, and that his influence has shaped my mind too much some times ;-)
This isn't the kind of advice you asked for, so I apologise in advance. I am a consultant, and I should know better than to look under rocks I wasn't asked to :)
It sounds to me as though the critical factor in the design decision was the speed of the dependency injection. Since that hasn't been measured, is this a case of premature optimisation? Questions I would be asking are:
- How long does injecting the dependency into a hundred or a thousand objects take?
- If it is too slow, can the injection itself be optimised sufficiently?
- Is there an alternative injection process you can use?
And another random thought: if it is the looking up the correct renderer that is too slow, perhaps you could inject a renderer lookup service instead, and lazily look up the renderer.
As I mentioned, if speed was the only issue, it's easy enough to do with custom types as we have done in some cases.
I've never met any piece of 'content' in the real world that could render itself as HTML. This doesn't even make sense for the majority of content (images, sounds). I can't think of a single reason why a 'Content' object would have a 'asHTML()' object. Such a conversion method should certainly depend only on the public state of the object and not any of its private state or capabilities. I find the entire idea of a 'Content' class pretty strange. What methods does it have? getId() and getTitle()? Since these aren't behavioral methods at all, at best you should only have a Content interface.
What's worse: I can't think of a single reason why IoC would be called upon to solve this problem. The task of 'Given a Content object find me a Renderer object that can render it' depends on a million different things--are we operating in headless mode, what medium are we rendering to, what format are we rendering to, are we rendering to a remote or local client, what level of detail are we rendering, etc etc. I also can't believe AOP mixins was recommended either. Talk about another solution which is no solution at all.
This is a great demonstration of why vodooo magic technologies like AOP and IoC should be avoided at *all* costs. They 1) don't make the system any simpler 2) often violate basic principles of good OO 3) more often than not, will be *misapplied* to produce a suboptimal design. It's just sad to watch Java developers go down this C++-like path to doom. It's laughable when people try to simiplify one set of acronyms (J2EE, JNDI) by introducing another set of acronyms (IoC, AOP).
The basic task-at-hand is designing simple, elegant, solid object-oriented systems. Let's try to stick to that rather than tossing in more crap. This is not to say AOP and IoC don't have their place but understand that they are *last resort* technologies. (I'm not even 100% sure they're last resort technologies. I see so many flaws with each, I'd say they should **never** be used except in a few specific cases where they are applied as design patterns). Rather than leaping on the latest bandwagon I really hope java devs can show a little more constraint when it comes to new acronyms.
Bo: That was precisely the kind of comment I asked people not to make.
Bo, if you have a case where the rendering depends on "a million things", then simply change the example I provided to:
String html = ((HtmlRenderable)content).toHtml(renderContext);"
renderContext can be arbitrarily complex, so it should be able to handle any strange things.
Without using a framework that knows about all the dependencies, I agree this looks like a perfect use of Strategy. I'll admit to having nagging snatches of comments by Rickard in Malmo last winter influence me, but since I have made no moves toward AOP per se, that isn't exactly my point of view either. I have solved the "millions of instances" issue (in c#) by making some assumptions that are generally true. You wouldn't have millions of unique combinations to render, so you can cache instances somewhere and clone them, so long as you can identify the consistent patterns that make them unique. In my case, this was much faster than constructing new instances each time. I have also had a much easier time explaining how to use strategy than I ever did getting less experienced developers correctly model classes with many competing concerns.
So what about postponing injection of the render component until you really need it?
>>But what if you’re pulling a few hundred or a thousand pieces of Content out of the database?
Is this actually ever the case?
Why would you pull a thousand Content objects from storage in one go?
However if for some reason you had to, is the IOC performance in this case as bad as you expect?
And if it is, how long will loading a thousand Content objects from the database take in total?
Is the IOC work even significant compared to network + db + object serialization time?
Is it worth the change in design ?
Cheers
Zohar
Well, adding another layer of indirection often helps. What about, instead of injecting Renderer, inject a factory that can make a Renderer on demand? The factory would be stateless, so all Context objects could share a single factory instance.
Zohar:
There are various situations in which we need to retrieve all pages within a space: getPages(token, spaceKey) in the remote API, for example. That can run into the thousands.
The overhead, as measured on my Powerbook, turns out to be 1.5ms per object, which is excessive, but still only really noticeable when you're accessing a lot of data.
As for "worth the change in design", I did a spike yesterday that seemed to indicate it was, but any more work will have to wait: I don't want to delay getting Swan out the door.