Our LDAP Client has a wrapper for creating a persistent search. In order to execute a persistent search, a polling mechanism has to call the client code with notifications of changes from the LDAP server. This means a threading library. The call looks something like this:
Here the call to GetApp is completely superfluos to the logic: we should not care where the ThreadPool comes from. Instead, LDAP client should either take a thread pool pointer in its constructor, or a thread pool should be passed as a parameter to CreatePersistentSearch. I prefer to resolve all dependencies like this at object construction time.
GetApp throws an exception if it is called prior to a call to AppImpl::Init. Init reads an XML based config file. So our dependencies now include both the config file and the xml parser on top of the App object. The LogObject and LogName are also initialized in the App.
What we are seeing here is how dependencies get entangled implicitly. In order to reuse the LDAP client, or to create a Unit Test for it, I have to initialize an App object, which is far beyond the scope of LDAP.
Continuing on looking at the ThreadPool, I see that the AppImpl actually creates the ThreadPoolWin32 object by passing the config file view to it, and the config file view is used to fetch values for the state of the thread pool value by value. Example:
_minIOThreads = 1;
_maxIOThreads = 2 * _maxWorkerThreads + 1;
The binding of the values to the object should be external of the constructor, as it is just one Initialization scheme. What if we want to reuse this object and read the values in from a database, or from a different config file format?
The LDAP Client should have a field that is the ThreadPool base class. For the Unit test, we could mock this out. Of course, maybe the persistent search itself should be its own class.