CS 214: Programming Languages
Spring 2009

Home|Syllabus|Schedule
<<|>>|ANTLR API|CITkit API|PolyD API|EasyMock API

Injecting a Dependency
Interpreters, Iteration 12

If you want to impress an employer, learn all you can about dependency injection. Those who practice it will be very impressed and hire you on the spot; those who don't practice it will be impressed anyway. (Note: you don't need to read the Wikipedia article yet; the job offer is not guaranteed; and you don't want to work for those you don't practice it.)

"Hard to Test" or "Too Much Responsibility"

Or both.

The HobbesInterpreter will become harder and harder to test as you add more operator algorithms. Presently, you're adding another assertion in HobbesInterpreterTest as well as a new test-case class (like AdditionAlgorithmTest, MultiplicationAlgorithmTest, etc.). This leads to a lot of testing redundancy.

The underlying reason why it's becoming hard to test a HobbesInterpreter is that it's taking too much responsibility for the map of operator algorithms. Its official job is to lookup and apply one of those algorithms, but should it be responsible for creating the contents of the map?

No, it shouldn't.

What if instead of creating the map in the HobbesInterpreter constructor you passed it in as an argument?

Let's start with a sanity check.

It's okay if you've named the instance variable something else as long as it's descriptive of its purpose.

HobbesInterpreter should have exactly one constructor. It should initialize an instance variable (which I called myOperators) as a new hash-map of Operators mapped to OperatorAlgorithms. Then it adds four operator algorithms.

If your code is different, refactor it into this state. Green bars all around.

Moving Code out of the Constructor

The allocation of the hash map and the putting of operator algorithms needs to move out of the constructor. The trouble is that the constructor does the allocation and putting directly on its instance variable. To extract a method, the constructor should do this work on a local variable and assign it to the instance variable at the very end.

Leaving the myOperators instance variable alone, turn the use of myOperators in the constructor into a local variable. (This is just a matter of putting a data type in front of its allocation.)

At the end of the constructor, use this.myOperators = myOperators; to initialize the instance variable.

Green bars all around.

Rename the local variable in the constructor (to something like operators). You can then turn this.myOperators into just myOperators.

Green bars all around.

Now Eclipse (or yourself, by hand) can extract a method.

Select the allocation and putting code in the HobbesInterpreter constructor. Use Refactor -> Extract Method to extract a new method (named something like createOperatorMap).

Green bars all around.

Not the Constructor's Responsibility

The constructor is still doing too much work: it's calling createOperatorMap() itself. (Yes, that's too much work!)

Make createOperatorMap() public and static.

Green bars all around.

This will make it easier (and possible) to call when necessary.

Delete the operators local variable in the constructor, and create it as a parameter of the constructor instead.

Yellow alert!

The compiler should complain. Before you look at the complaints, can you predict where the errors will be and what the complaints are?

For each compiler error, replace new HobbesInterpreter() with new HobbesInterpreter(HobbesInterpreter.createOperatorMap()).

Green bars all around!

How accurate were you predictions about the errors? Can you explain why you were wrong (if you were)?

Still Too Much

Some might argue that createOperatorMap() could stay in HobbesInterpreter since it'll be the default operator map anyway. Others might just want to prove that HobbesInterpreter doesn't have to worry about creating that operator map at all.

Creating objects for other objects to use is considered the job of a factory.

  1. Create a new class named HobbesInterpreterFactory.
  2. Use Refactor -> Move... to move createOperatorMap() to HobbesInterpreterFactory. (Eclipse will automatically move the method and update the places where you call the method.)

Green bars all around.

Observe how HobbesInterpreter knows absolutely nothing about ADD, MULTIPLY, or any other specific Operator!

Definitions and the Future

Incidentally, adding operator as a parameter to the HobbesInterpreter constructor is a refactoring that Eclipse could help you with: "Change Method Signature". Feel free to use that in the future, but it glosses over a lot of details I wanted you to see.

The more important term for this iteration is "dependency injection". A HobbesInterpreter has a dependency on a map of operator algorithms. If you hard code the dependency in the class (i.e., in the constructor), you've locked yourself into that one dependency.

By requiring that the map be passed in as a argument to the constructor, anyone can "inject" any map that they want. This is some incredible flexibility without much work!

One good test to see if you're doing good dependency injection is to look at your constructors. They should receive parameters and set instance variables. They should not do any computations at all.

There are other benefits. HobbesInterpreter has fewer responsibilities now. Smaller is almost always better, and you've broken two responsibilities into two different places.

And as I promised from the very beginning, this change will make testing a HobbesInterpreter easier. However, you should be very skeptical about this since it's not easier (yet), and this iteration is obviously coming to a close. While the testing will be easier, there's a new library to set up and learn—enough work to warrant a separate iteration!