"If all you have is a hammer, everything looks like a nail"
- Abraham Maslow
To make it clear - I don't claim that DI is evil. I think it's really useful tool. Just a tool like a hammer. Unfortunately very often I can see DI being misused. People tend to put DI in each piece of the application what is often either an overkill or at least unnecessary complication.
One of the bad practices I've recognized is
programming in XML. That happens for example when (hierarchies of) objects (including abstract objects) are defined in Spring configuration files and then used as injected prototypes in Java code which is coupled to their properties. Simple and clear solution is to introduce factory class to create objects directly in Java code.
That's rather rare case but don't worry there are also other more spectacular like for example
Anemic Domain Model. Which is very common and appreciated by some people. It decouples basically everything including the data and the logic. In my opinion DI encourages to use this approach because it enforces to drop the encapsulation and lets to do anyone anything - you have to set the properties somehow, right?
Of course there are solutions like constructor injection and factory beans but they are used rarely.
As far as I remember Spring 1.0 has been announced as a holy grail of JEE development. People dropped EJB2 and started learning Spring. I must admit EJB2 didn't have much in common with object oriented programming as well. The goal of Spring was to simplify JEE project and it has been achieved. Unfortunately Spring has inherited EJB2 functional approach - or Spring users did that.
Did you ever seen a Java project written by someone who is dazzled by DI? It's easy to recognize such a project. If during the review you have to analyze call hierarchies defined mostly in DI configuration and distributed among several XML files - that's it.
DI is not a golden hammer of software development and we also must remember about the design patterns. The art is to join them together and find the balance to not see everything as a nail anymore.
2 komentarze:
"If all you have is an object, everything looks like a dependency."
-- Niclas Hedhman, 2004
You are right that DI is not a golden hammer, silver bullet (who/what is the werewolf?) or a magic trick to get your code in order. Quite contrary as you point out. Overuse, without scope and context management is a recipe for the very popular Bowl/Ball of Mud.
Now, Rickard Oberg and I have long been struggling with architectural questions around good Java applications, he coming from the EJB failure and I from the Apache Avalon failure. Our combined effort of "assume everything is wrong" and "challenge everything" has (IMVHO) produced a lot of "forgotten" insights into Java development.
The result is seen in Qi4j and what we call Composite Oriented Programming.
In respect to this article, Dependency Injection is central, BUT it is scoped and limited. Scope means "from where can this come from" and with limited we mean "not anything can be injected".
@Service BankTeller teller;
That is injection of a BankTeller service. Clear, concise, and since we make the declaration in code, we don't need to juggle with the XML.
JSR-330 also embraces the scope concept, but insist on doing so at the declaration and not at the injection;
@Inject BankTeller teller;
Is it a service, an entity or a plain value?? You must go to the declaration of BankTeller to find out. We think that simplifies writing, but makes reading much harder, and reading code is what needs to be simpler, not writing it.
Another mechanism that we are using heavily in Qi4j, that isn't thought of as Dependency Injection is the "Builder Pattern".
In Qi4j, you simply do;
UnitOfWork uow = factory.currentUnitOfWork();
EntityBuilder<User> builder = uow.newEntityBuilder( User.class );
User proto = builder.instance();
proto.name().set( "Niclas Hedhman" );
:
:
User user = builder.newInstance();
where,
public interface User
{
Property<String> name();
}
Now, since User is a interface it can not be instantiated. But, we provide a way to make declarations of visibility and mapping between a domain interface and a Composite (kind-of object),
public interface UserEntity extends User, EntityComposite
{}
and
public void assemble( ModuleAssembly module )
{
module.addEntities( UserEntity.class );
}
This gives us really simple mechanisms for 'injection' of what type is going to be used, for instance for TDD, but could be a 'sandboxed' environment, 'staging', 'secured' and so forth.
Builder patterns can easily be deployed for many of the Dependency Injection tasks without framework support or even if Spring Framework is the 'base architecture' (shrug) to minimize the amount of XML that needs to be juggled.
Spring has a large set of problems as a DI framework, from the idiotic idea that the presence of getAbc()/setAbc() makes Abc a property, to that everything should be injected, therefor institutionalizing the idea that object creation is best left to clients (*shrug*).
I didn't know Qi4j before. After reading your comment I know I need to have a closer look on it.
And what are the werewolfs? There are several like wrong application architecture, ugly design, misuse of the tools.
Post a Comment