5

I'm trying to get dependency inversion, or at least understand how to apply it, but the problem I have at the moment is how to deal with dependencies that are pervasive. The classic example of this is trace logging, but in my application I have many services that most if not all code will depend on (trace logging, string manipulation, user message logging etc).

None of the solutions to this would appear to be particularly palatable:

  • Using constructor dependency injection would mean that most of the constructors would have several, many, standard injected dependencies because most classes explicitly require those dependencies (they are not just passing them down to objects that they construct).
  • Service locator pattern just drives the dependencies underground, removing them from the constructor but hiding them so that it's not even explicit that the dependencies are required
  • Singleton services are, well, Singletons, and also serve to hide the dependencies
  • Lumping all those common services together into a single CommonServices interface and injecting that aswell a) violates the Law of Demeter and b) is really just another name for a Service Locator, albeit a specific rather than a generic one.

Does anyone have any other suggestions for how to structure these kinds of dependencies, or indeed any experience of any of the above solutions?

Note that I don't have a particular DI framework in mind, in fact we're programming in C++ and would be doing any injection manually (if indeed dependencies are injected).

Steven
  • 166,672
  • 24
  • 332
  • 435
Tom Quarendon
  • 5,625
  • 5
  • 23
  • 30
  • Do what ever allows you to get the job done. – Petah Oct 24 '12 at 20:26
  • 2
    @Petah OK, so I'll do it with global variables and functions. That wasn't really the point of the question. I was hoping an experienced Dependency Injectionist might have some useful wisdom. – Tom Quarendon Oct 25 '12 at 06:14
  • There are DI frameworks for C++. You might want to take a look: http://stackoverflow.com/questions/79825/can-anyone-recommend-a-ioc-container-for-c – Steven Oct 25 '12 at 20:20
  • I know there are DI frameworks for C++, however using them at this time is not practical for us. – Tom Quarendon Oct 25 '12 at 20:30

3 Answers3

2

Service locator pattern just drives the dependencies underground, Singleton services are, well, Singletons, and also serve to hide the dependencies

This is a good observation. Hiding the dependencies doesn't remove them. Instead you should address the number of dependencies a class needs.

Using constructor dependency injection would mean that most of the constructors would have several, many, standard injected dependencies because most classes explicitly require those dependencies

If this is the case, you are probably violating the Single Responsibility Principle. In other words, those classes are probably too big and do too much. Since you are talking about logging and tracing, you should ask yourself if you aren't logging too much. But in general, logging and tracing are cross-cutting concerns and you should not have to add them to many classes in the system. If you correctly apply the SOLID principles, this problem goes away (as explained here).

Community
  • 1
  • 1
Steven
  • 166,672
  • 24
  • 332
  • 435
  • I was expecting an answer of "logging is a cross cutting concern and therefore you just use aspects", and nearly added something to the question for that. Practically though you do want trace logging in the code, moving it away from relevant source lines just seems unnecessary. – Tom Quarendon Oct 25 '12 at 15:15
  • Although it's possible that there are violations of the SRP I don't completely buy that argument. However even if it's true, all that you end up doing is having loads more classes, each of which has fewer dependencies, but you then have to build a complex tree of all those things, total number of arguments passed to constructors actually increases, actually constructing that graph is harder than before. – Tom Quarendon Oct 25 '12 at 15:18
  • @TomQuarendon: Are you sure you read the [referenced answer](http://bit.ly/TDu43U), and especially [the article](http://bit.ly/s7tGEH) that's referenced there completely? The described design explicitly tries to prevent having 'loads more classes', by defining just a few decorators that contain that logic. And don't forget that many small classes isn't a bad thing. When designed correctly, it actually makes the system simpler. And don't forget that constructing complex object graphs is not something you normally do by hand. This is this where DI frameworks are for. – Steven Oct 25 '12 at 17:20
  • I read the referenced article yes, though only now to I see the reference to the article you mention (at the bottom in a comment). We are using C++, and aren't using a DI framework, I'm just trying to design the best system I can and apply dependency inversion in he best way I can. I didn't way that small classes were bad, just that lots of small classes each with smaller number of dependencies just means your object construction is harder, the system as a whole still has the same number of dependencies. – Tom Quarendon Oct 25 '12 at 17:58
  • The referenced answer is specifically about .net, but similar principles can be used for c++. And indeed for the examples you give the the answer (security, auditing, performance logging) decorators are fine. A number of factors mitigate against in c++ though. There's not doubt that many, small, heap allocated classes causes a performance problem. Also for the things I'm thinking of decorators just aren't practical. Now it's possible that that is because the design isn't right to start with, but I'm not convinced. – Tom Quarendon Oct 25 '12 at 18:01
  • I'm sorry @TomQuarendon, I didn't know you are working with C++. I'm not that familiar with C++ but I imagine the C++ runtime to be much less powerful (think reflection, metadata, dynamic code generation, expression trees) than that of .NET. Although the principles the article talks about are as applicable to C++, the examples and the way types are wired together (using generic typing, reflection and the rich type system) might be very specific to .NET. No doubt about it that the proposed design is hard to fit into an already existing application. – Steven Oct 25 '12 at 20:11
  • Though, your argument about heap allocations is -as I see it- not an issue. If you are in a really constraint environment where memory is an issue, you can design your commands as value types (structs) and design your command handlers (and other services) as singletons. This means that you create all services once (probably during application start-up, with decorators and all) and you the rest of the application could free from doing heap allocations. Creating an application under these tight constraints is -without any doubt- hard, but DI doesn't make this any harder. – Steven Oct 25 '12 at 20:19
  • I sure hope you mean singleton with a lower case s. It's unclear from your article whether you think that anything that mutates any kind of state anywhere should be modelled as a command and handler. Admittedly I haven't tried it, but I think that doing that, modelling anything that changed state as a concrete command and a handler interface, and creating the handler using some kind of abstract factory would just be impractical. – Tom Quarendon Oct 25 '12 at 20:40
  • I'm talking about the singleton DI lifestyle, not the singleton design pattern :-) – Steven Oct 25 '12 at 20:55
  • *"It's unclear [...] whether you think that anything that mutates [...] should be modelled as a command"*. I wrote that in a [follow up](http://www.cuttingedge.it/blogs/steven/pivot/entry.php?id=92). A little quote: *"If we want, we can model every single operation as a query (or command). Do we really want to do this? No, definitely not"*. – Steven Oct 26 '12 at 08:24
  • I think I had a revelation this morning. Without a DI container, you have no choice basically but to use a ServiceLocator pattern. You inject abstract factory interfaces where necessary to avoid dependencies on the actual concrete classes, but the implementation of those factories would normally call the DI container, but in the absence of that then have to use a Service Locator. In anything other than a simple system where the entire object graph gets created in main, you're creating the root factories in main and passing them the SL. They then in turn create further factories etc. – Tom Quarendon Oct 26 '12 at 13:19
2

The Dependency Inversion principle is part of the SOLID Principles and is an important principle for among other things, to promote testability and reuse of the higher-level algorithm.

Background: As indicated on Uncle Bob's web page, Dependency Inversion is about depend on abstractions, not on concretions.

In practice, what happens is that some places where your class instantiates another class directly, need to be changed such that the implementation of the inner class can be specified by the caller.

For instance, if I have a Model class, I should not hard code it to use a specific database class. If I do that, I cannot use the Model class to use a different database implementation. This might be useful if you have a different database provider, or you may want to replace the database provider with a fake database for testing purposes.

Rather than the Model doing a "new" on the Database class, it will simply use an IDatabase interface that the Database class implements. The Model never refers to a concrete Database class. But then who instantiates the Database class? One solution is Constructor Injection (part of Dependency Injection). For this example, the Model class is given a new constructor that takes an IDatabase instance which it is to use, rather than instantiate one itself.

This solves the original problem of the Model no longer references the concrete Database class and uses the database through the IDatabase abstraction. But it introduces the problem mentioned in the Question, which is that it goes against Law of Demeter. That is, in this case, the caller of Model now has to know about IDatabase, when previously it did not. The Model is now exposing to its clients some detail about how it gets its job done.

Even if you were okay with this, there's another issue that seems to confuse a lot of people, including some trainers. There's as an assumption that any time a class, such as Model, instantiates another class concretely, then it's breaking the Dependency Inversion principle and therefore it is bad. But in practice, you can't follow these types of hard-and-fast rules. There are times when you need to use concrete classes. For instance, if you're going to throw an exception you have to "new it up" (eg. threw new BadArgumentException(...)). Or use classes from the base system such as strings, dictionaries, etc.

There's no simple rule that works in all cases. You have to understand what it is that you're trying to accomplish. If you're after testability, then the fact that the Model classes references the Database class directly is not itself a problem. The problem is the fact that the Model class has no other means of using another Database class. You solve this problem by implementing the Model class such that it uses IDatabase, and allows a client to specify an IDatabase implementation. If one is not specified by the client, the Model can then use a concrete implementation.

This is similar to the design of the many libraries, including C++ Standard Library. For instance, looking at the declaration std::set container:

template < class T,                        // set::key_type/value_type
           class Compare = less<T>,        // set::key_compare/value_compare
           class Alloc = allocator<T> >    // set::allocator_type
           > class set;

You can see that it allows you to specify a comparer and an allocator, but most of the time, you take the default, especially the allocator. The STL has many such facets, especially in the IO library where detailed aspects of streaming can be augmented for localization, endianness, locales, etc.

In addition to testability, this allows the reuse of the higher-level algorithm with entirely different implementation of the classes that the algorithm internally uses.

And finally, back to the assertion I made previously with regard to scenarios where you would not want to invert the dependency. That is, there are times when you need to instantiate a concrete class, such as when instantiating the exception class, BadArgumentException. But, if you're after testability, you can also make the argument that you do, in fact, want to invert dependency of this as well. You may want to design the Model class such that all instantiations of exceptions are delegated to a class and invoked through an abstract interface. That way, code that tests the Model class can provide its own exception class whose usage the test can then monitor.

I've had colleagues give me examples where they abstract instantiation of even system calls, such as "getsystemtime" simply so they can test daylight savings and time-zone scenarios through their unit-testing.

Follow the YAGNI principle -- don't add abstractions simply because you think you might need it. If you're practicing test-first development, the right abstractions becomes apparent and only just enough abstraction is implemented to pass the test.

zumalifeguard
  • 8,648
  • 5
  • 43
  • 56
2
class Base {
 public:
  void doX() {
    doA();
    doB();
  }

  virtual void doA() {/*does A*/}
  virtual void doB() {/*does B*/}
};

class LoggedBase public : Base {
 public:
  LoggedBase(Logger& logger) : l(logger) {}
  virtual void doA() {l.log("start A"); Base::doA(); l.log("Stop A");}
  virtual void doB() {l.log("start B"); Base::doB(); l.log("Stop B");}
 private:
  Logger& l;
};

Now you can create the LoggedBase using an abstract factory that knows about the logger. Nobody else has to know about the logger, nor do they need to know about LoggedBase.

class BaseFactory {
 public:
  virtual Base& makeBase() = 0;
};

class BaseFactoryImp public : BaseFactory {
 public:
  BaseFactoryImp(Logger& logger) : l(logger) {}
  virtual Base& makeBase() {return *(new LoggedBase(l));}
};

The factory implementation is held in a global variable:

BaseFactory* baseFactory;

And is initialized to an instance of BaseFactoryImp by 'main' or some function close to main. Only that function knows about BaseFactoryImp and LoggedBase. Everyone else is blissfully ignorant of them all.

Robert Martin
  • 2,825
  • 1
  • 17
  • 6
  • Making the factory a global variable slightly subverts dependency injection, but I get your point. – Tom Quarendon May 23 '13 at 06:57
  • Actually thinking about it, this isn't really what I mean by logging. I don't buy the "decorator" or "aspect" approach to internal trace logging. This approach is fine if what you want is entry/exit/argument/return tracing, but that's rarely what I want. What is much more useful is internal tracing of the execution of some method. doing it this way you can only write trace entries at the public interface points to the method. If the method implementation is complex and has many private sub methods, then you can't write any trace entries during its execution. – Tom Quarendon May 25 '13 at 06:37
  • What I mean by user logging is output targetted at the user. I want to abstract where that goes, I don't want to hard code writing to std::cout, System.out etc, I don't want a global variable, and I don't want to have to pass an object around to all and sundry. I think I understand now how pure dependency injection would solve this, and will write up when I can. – Tom Quarendon May 25 '13 at 06:40