Should I make concrete class dependencies explicit through constructor injection in my classes?
I am doing the design of a small project where I didn't use programming against interfaces for all my classes. I found that some classes would hardly ever be needed to change, so I let them be referenced by th开发者_开发技巧eir client classes as concrete classes.
So, let's say we have ClassB
that'll be consumed be consumed by ClassA
:
class ClassB {
}
My question is, should I create ClassB
in ClassA
, or should I pass that responsability "up" in the hierarchy? I'll depict both cases:
class ClassA {
private ClassB classB;
public ClassA() {
this.classB = new ClassB();
}
}
or
class ClassA {
private ClassB classB;
public ClassA(ClassB classB) {
this.classB = classB;
}
}
I'd like to hear about how would you do it and what'd be the rationale behind it!
Thanks
Some advantages of your first option (A creates B):
- The user of class A needs to know nothing about B or how to create & configure it. This makes for a simpler programming model.
- B can also be made private/internal, so that users of your library don't need to wade through lots of helper classes they don't need to see.
- You can change the way A works to not use B at all without breaking calling code.
- Encapsulation - no one gets to tamper with B from the outside while A is running
Some advantages of your second option (A takes a dependency on B):
- The user of class A has full control over the B object - they can configure it differently, they can pass the same one into multiple instances of A if they want.
- It opens the door for different implementations to be passed in (if B is an interface or base class) which is of great value for unit testing, or extensibility.
- update: if in the future, it becomes more complicated to create B (e.g. B has some dependencies of its own), then that can be managed outside without needing to make changes to A.
Note that it is also possible to create a "best of both worlds" solution by using what is sometimes called "poor man's dependency injection":
class ClassA {
private ClassB classB;
public ClassA(ClassB classB) {
this.classB = classB;
}
public ClassA() : this(new ClassB()) {
}
}
It depends on the implementation, there is no general answer. If A has all the relevant data and knowledge to initialize B, then it could initialize it.
If you don't want A to know how to initialize B or you don't want to give A all the data for the initialization, then you should create it externally.
you'd better inject interface which implemented by ClassB.
class ClassA {
private InterfaceB B;
public ClassA(InterfaceB B) {
this.B = B;
}
}
class classB: InterfaceB
{
}
Please note that InterfaceB may be a regular class as base class for your derived ones. In this case it still works as interface.
In the former A
is taking the full responsibility of class B
instantiation. So, there is nothing which can go wrong. A
knows B
like anything. In case A
wants few of the B
properties to be set before invoking any method on it. It can do that by setting those just after intialisation.
In the latter, A
can't be that sure of B
.
Now, the choice is of course yours.
A couple of advantages for the second option (passing the object via the constructor, i.e. inverting control) that haven't been mentioned so far:
1) You can use a dependency injection tool to inject the classB object. Although it can't inject different implementations because you are locked to the concrete class, it can specify the objects life-cycle (singleton, per-request, per-thread) and any constructor parameters used to instantiate the object.
2) If you always pass dependencies in the constructor (inversion of control) then it is arguably clearer which dependencies each class has at a glance.
精彩评论