Amumu
Amumu

Reputation: 18572

Dependency Injection mentioned by C++ Google Mock guide

What do they mean with Dependency Injection (Inversion of Control) in this context (Google Mock):

Let's look at an example. Suppose you are developing a graphics program that relies on a LOGO-like API for drawing. How would you test that it does the right thing? Well, you can run it and compare the screen with a golden screen snapshot, but let's admit it: tests like this are expensive to run and fragile (What if you just upgraded to a shiny new graphics card that has better anti-aliasing? Suddenly you have to update all your golden images.). It would be too painful if all your tests are like this. Fortunately, you learned about Dependency Injection and know the right thing to do: instead of having your application talk to the drawing API directly, wrap the API in an interface (say, Turtle) and code to that interface:

class Turtle {
  ...
  virtual ~Turtle() {}
  virtual void PenUp() = 0;
  virtual void PenDown() = 0;
  virtual void Forward(int distance) = 0;
  virtual void Turn(int degrees) = 0;
  virtual void GoTo(int x, int y) = 0;
  virtual int GetX() const = 0;
  virtual int GetY() const = 0;
};

What does it have to do with DI when adding another layer between application code and drawing API by a class? In many Java example about Dependency Injection, usually an object should not be concretely created inside a class. Rather, it should be created elsewhere to separate the implementation coupling between two objects. For example (source from codeproject):

enter image description here

Solution:

enter image description here

As I searched through the answers about DI on Stackoverflow, usually it is asked in the context of Java. Some example used the Java GUI. Usually the examples is so simple and so obvious that I failed to see its significance, except for having better design with less coupling. However, what I want to learn is the meaning behind it. As defined in wiki, Inversion of Control (IoC) means you invert the flow of control of code. So, how does it apply to the Google case? How the actual flow is inverted compare to procedural style? I thought that code is executed sequentially line by line from top to bottom, not from bottom to top?

Upvotes: 3

Views: 4351

Answers (4)

reece
reece

Reputation: 8155

Wikipedia has good definitions of these:

  1. http://en.wikipedia.org/wiki/Inversion_of_control
  2. http://en.wikipedia.org/wiki/Dependency_injection

Dependency Injection is just a fancy way of saying that the class interacts with another via an interface (the Graphics API) and that it provides a way of changing what the interface points to (i.e. injecting a dependency on another class).

For Inversion of Control, Wikipedia mentions things like the Factory pattern.

It also mentions setter injection (changing an interface implementation using a setter function), construction injection (setting the interface implementation from the constructor) or interface injection (requesting an interface implementation from another interface) and notes that these are types of Dependency Injection.

This is what is happening here -- the program can change the drawing API of the turtle program (Dependency Injection) using a setter method (Inversion of Control).

This allows you to have a test class like this:

struct DrawingTester : public DrawingInterface
{
    void move_to(long x, long y) { printf("moveto %d %d\n", x, y); }
    void line_to(long x, long y) { printf("lineto %d %d\n", x, y); }
};

and drive it through a test program:

int main(int argc, char **argv) {
    DrawingTester drawing;
    Turtle t;
    t.setDrawingApi(&drawing);
    t.runProgramFromFile(argv[0]);
    return 0;
}

You can then have the turtle/logo test programs with expected output from the DrawingTester. For example:

# test1.logo

MOVE 5, 7

# test1.calls

moveto 5 7

and drive this through a test suite (e.g. https://github.com/rhdunn/cainteoir-engine/blob/master/tests/harness.py and https://github.com/rhdunn/cainteoir-engine/blob/master/tests/metadata.py).

Upvotes: 3

sergio
sergio

Reputation: 69037

It seems to me that you are kind of "equating" dependency injection and inversion of control.

Now, as far as I understand, dependency injection is a design pattern (roughly speaking) where you "injects" a pointer to an object into another object; this will make the second dependent on the first (i.e., it needs to know the former's interface, and will be affected by changes at is interface level). So, it is pretty a generic concept that can help in many different context.

In this sense, dependency injection is just a way to implement inversion of control. This happens, for example, when you inject callbacks into an object, so that they are called at the proper moment.

In your specific example, I doubt that dependency injection is used as a way to get inversion of control, since it does not seem to me that control is inverted (like it happens, e.g., when you have callbacks or with frameworks).

It is more a case, as you correctly say, of adding an intermediate layer for better decoupling (which can also be obtained through dependency injection).

Upvotes: 1

Sylvain Defresne
Sylvain Defresne

Reputation: 44553

The inversion in "Inversion of Control" is not an inversion in the order of the code execution, but on object creation. In a architecture that does not use "Dependency Injection", the objects will create their dependencies themselves. On the contrary, when using DI, the object will receive their dependency from the outside, and use interface (abstract class in C++) to get independence from the implementation.

In the LOGO example, by using an interface and a setter instead of creating the API wrapper directly, you allow the code that call you to supply its implementation of the interface. That way, it is easier to test your code (by providing a mocking implementation that record all the call made) or to use another implementation.

Upvotes: 2

Doc Brown
Doc Brown

Reputation: 20044

DI means here, that your program does not have a hardcoded dependency to the LOGO-like api, but this dependency is "injected" at run time through the interface. This way, one can replace the api by a Mock API for purposes of unit testing.

"Inversion of Control" means here: if there is a function in your program needing something like a "graphics context object" of the LOGO-like API, it should not instantiate that object by itself. Instead, it should get the context object given as a parameter (typed to the "Turtle" interface), and the object creation can be done, for example, by the IoC framework.

That will work the same way as you have shown in your "customer-address" example above, replace clsAddress by clsGraphicsContext.

Upvotes: 3

Related Questions