I have been reading Effective Java and I have some concerns regarding the first Item "use static factory method instead of constructor" in relation to TDD and dependency injection.
The item says that you should avoid having public/protected/default constructor and expose it using static factory. I agree with all the advantages related to using static factories like factories can have name, you can return subtype, you can reduce verbosity etc. But, I think in disadvantages Joshua missed TDD because having static factories in your code will lead to tight coupling and you can't mock the class using it. We will not be able to mock the class which will be having static factories. So, it hampers test driven development.
Second point, I think he missed that in todays enterprise development most of the applications use one or another dependency injection container. So, when we c开发者_如何学编程an inject dependencies using DI so why should I use it.
Please explain how it applies to today's Java enterprise development which includes DI and TDD.
The DI engine is the factory.
Joshua Bloch understands DI well enough. I think this is an artifact of history, because DI's ascent came after the first edition of "Effective Java".
"Effective Java" was published in 2001. Martin Fowler coined the term in 2004. Spring's 1.0 release came in March 2004.
Joshua Bloch didn't modify that chapter for the second edition.
The point is the coupling that "new" introduces. Anyone who understands that and factories will easily make the leap to DI engines. The point is that his statements regarding "new", and the remedy of using factories, is still true.
I see 2 separate issues here :
- static factories vs using new()
- dependency injection
When using new your code is just as tightly coupled as using a static method, actually even worse since the static factory can do some magic and return some specific implementation as long as it is a subclass or implementation of an interface.
With dependency injection the principle is also called the Hollywood Principle : "Do not call us, we'll call you". So in that philosphy you should not call new() or the static factory in your code, but have an external thing do that for you, either the DI framework or the unit test. This has nothing to do with factories or the use of new, as that is done somewhere else.
In that case factories are better because you can inject a test factory under your control. With new this is not possible (without doing weird things to the classpath like hiding the implementation with dummy objects in the test classpath, which I do not recommend btw).
I had the same concern you have, and that's how I found your question.
You say:
because having static factories in your code will lead to tight coupling and you can't mock the class using it
the book suggests that you should expose the constructor of your class with a static method (api design). It doesn't suggest that you "hardcode" the static call all over your application (api usage).
Suppose you are using Guice for DI, and your interface is called Connection
, you could do:
bind(Connection.class).toInstance(Connections.makeASpecificConnection(params));
And then your usual @Inject Connection connection;
Of course this is if your connection is a Singleton. If it's not, you could inject an Abstract Factory with an implementation that creates instances calling the static method of your class, but this might be way overkill and you'd be better using Guice alone.
Remember that this book is not targeted primarily at building huge enterprise applications, although its still very helpful. Quote from the preface of the book:
While this book is not targeted solely at developers of reusable components, it is inevitably colored by my experience writing such components over the past two decades. I naturally think in terms of exported APIs (Application Programming Interfaces), and I encourage you to do likewise.
精彩评论