The general approach to DI that I see in answers like So Singletons are bad, then what? encourages business objects that collaborate with other objects to (a) not directly create those instances and (b) have them passed in at construction. I can understand (a), but not (b). This seems to occur most often in response to overuse of Singletons. But why not just have a modified approach to singleton:
class SingleInstance {
virtual foo();
virtual bar();
static SingleInstance getInstance() {
if(instance_ == null) {
instance_ = new SingleInstance();
}
return instance_;
}
void setMockInstance(SingleInstance s) {
assert(instance_ == null);
instance_ = s;
}
static SingleInstance instance_;
}
So now this is no longer a Singleton with a capital S (per Misko Hevery) but in this case still enforces one instance. All code that wants to access the single instance can still call S::getInstance() without cluttering up their constructors by explicitly requiring the instance to be passed in. The default can still be still lazily initialized in the production code, but for test can still be mocked.
In the referenced answer the first benefit of DI is listed as:
It makes the code easier to read; you can clearly understand from
the interfaces exactly what data the dependent classes depend on.
But why is that not a violation of encapsulation? Do I really need to know from the public interface everything that accesses the SingleInstance/Database/etc?
Assume you have a Database and 30 TableGateway classes responsible for CRUD operations on those tables. In the DI approach TableGateway constructors would accept the Database on in its constructor. Then a business logic class would accept the tables it collaborates with/uses:
class BusinessLogic {
BusinessLogic(Table1 t1, Table2 t2, Table3 t3);
void doBusiness() {
t1_.query(...);
t2_.insert(...);
t3_.update(...);
}
}
How is that churn of explicit dependencies in the constructor advisable?