Reputation:
I am trying to convert some old C++ code into more testable form. To conform to the Dependency Inversion Principle (DIP), I will use dependency injection in many cases.
My question is how best to instantiate the concrete classes. There are something like 100 of my own classes in this code. In his book Clean Architecture, Robert Martin argues that these classes are volatile and should be instantiated in factories. However (see p. 90 of the book) this would require 4 classes per class that I want to instantiate. That would mean 400 classes.
Let's say for the sake of illustration that the old code has a class A (which instantiates and uses classes A1 through A5), B (B1 through B10), and C (classes C1 through C3).
In the new code, how and where would you suggest instantiating all of the concrete classes? I would especially be interested in hearing from anyone who has dealt with this sort of issue in large C++ programs. Thanks.
Bruce
Upvotes: 0
Views: 273
Reputation: 172826
When applying the Dependency Inversion Principle, we delay the construction of graphs of collaborating components (a.k.a. object graphs) to the last responsible moment. Since we want to minimize the coupling between components and even individual libraries, we move the construction of those object graphs to the most volatile part of the system. This is the start-up assembly. The part within the start-up assembly that is responsible of composing and constructing these object graphs is typically referred to as the Composition Root.
In the new code, how and where would you suggest instantiating all of the concrete classes?
You should construct all your concrete components (the classes that contain the application's interesting behavior) inside the Composition Root.
In some managed environments, like Java and .NET, the availability of APIs that enable us to query the application's metadata at runtime (a.k.a. Reflection), allows relationships between abstractions and implementations to be declared at compile-time, and components to be built up dynamically at runtime using reusable libraries typically called called DI Containers.
C++ might not have this ability to query metadata at runtime. In such environment, you make use of Pure DI, which simply means: you new
your classes by hand inside your Composition Root.
Notice that nowhere in my description I used the word "factory". The need to specify factory abstractions that return other application abstractions should not be the norm, but the exception. You might see your Composition Root as one big factory, but nothing in the application depends on the Composition Root. The Composition Root on the other hand, depends on everything.
So your application classes hardly need to depend on factory abstractions. If a class requires a different service, it should just get this injected into its constructor directly, instead of injecting a factory that can resolve that service.
Upvotes: 2