Dependencies are injected in constructor but it is not recommended to use them.
I think the author is saying the opposite of that with a focus on loose coupling. He's quoted as saying:
"The Constructor Injection design pattern is a extremely useful way to implement loose coupling"
Loose coupling is the core of good software design. It makes classes reusable, extendable and testable.
Initialize phase brings complexity and should be avoided.
Yes it does, but that doesn't mean it can always be avoided. Constructors do not have a return value, and not all languages support throwing of exceptions. Some books argue that a constructor should always be successful, and I agree with this rule.
Here is some source code examples of both styles. Both approaches work, but which one is better?
try
{
FileReader f = new FileReader("something.txt");
String str = f.read();
}
catch(FileNotFound e) {...}
or
FileReader f = new FileReader("something.txt");
if(f.exists())
{
String str = f.read();
}
The first example has the FileReader
object dependant upon an external resource. If it can not access it, then it fails during the constructor. This creates dependency beyond the controller of the programmer, and also unit testing.
In the second example there is no dependency. The object is allowed to be created even if the resource doesn't exist. The dependency is now up to the programmer to enforce.
So how does this relate to initializing
an object. It's very simple. Who should be responsible? The object or the programmer. That's up to the author of the object. He/she might have good reasons to give up control of constructing the object to the programmer using it.
If initializing
an object is a complex task, then you localize that code into a factory class so you have one place to go to make changes.
Are not methods like Connection.Open() just another name for Initialize?
The method Connection.Open()
is an initializer only if Connection.Read()
fails if Open()
was not called.
Here is the problem the author is talking about.
Connection con = new Connection();
con->Read(); // this will fail, Open() was not called
To fix the above code. You have to write this, and this is a bad design.
Connection con = new Connection();
con->Open("192.168.1.1"); // bug fix, forgot to call Open()
con->Read();
I've read many comments in source code by programmers who write "bug fix, forgot to call X(...)". The argument is that the bug was avoidable in the first place. Had the author of the Connection
class not used an initializer.
Here is the solution to the problem.
Connection con = new Connection("192.168.1.1");
con->Read();
Now, how you handle a failed connection is answered higher up in my answer. Either the constructor throws an exception, or the programmer has to call isOpen()
before read()
.
So can anyone describe a good initialization pattern in the context of Dependency Injection that addresses the concerns Mark Seeman brings up?
It might be hard to understand, but the answer is in the Single Responsibility Principle
.
For my example with the Connection
object. It broke the SRP rule. The Connection
object opens and reads from the resource. That's two different responsibilities. We can fix this by fracturing the object into multiple pieces each with their own responsibilities.
Here is an example;
try
{
SocketAddress addr = new SocketAddress("192.168.1.1");
try
{
Socket s = new Socket(addr);
try
{
SocketReader r = new SocketReader(s);
if(r != null)
{
String str = r->Read();
}
} catch(ReadFailure e) {..}
} catch(ConnectionFailure e) {..}
} catch(BadAddress e) {..}
Each object is responsible only for one thing.
SocketAddress
will only construct successfully if the address is valid.
Socket
will only construct if it can make a connection to the address.
SocketReader
only work if it can read.
As you can see. You have to write a lot more source code, and this is why we often see dependency injection avoided. It's extra work on the part of the programmer.