by Miško Hevery
We talked about how it is important to separate the new operators from the application logic. This separation forces your code to have factories which are responsible for wiring your application together. By separating these responsibilities the tests can always wire together a subset of an application with key components replaced for friendlies making testing easier and more focused.
Let's look at a sample factory
class CarFactory { Car create() { return new Car( new EngineCompartment( new Engine(), new Door(new PowerWindow()), new Door(new PowerWindow()), new PowerSeat(), new PowerSeat() new ManualTransmission(), new PowerSteering(), new Battery() ), new Cabin( new Door(new PowerWindow()), new Door(new PowerWindow()), new PowerSeat(), new PowerSeat() ), Arrays.asList( new Wheel(new Tire(), new Rim()), new Wheel(new Tire(), new Rim()), new Wheel(new Tire(), new Rim()), new Wheel(new Tire(), new Rim()) ) ); } }
new Door(new PowerWindow()), new Door(new PowerWindow()), new PowerSeat(), new PowerSeat()
Car(EngineCompartment ec, Cabin c, List ws); EngineCompartment(Engine e, Transmission t, Steering s, Battery b); Cabin(Door driverDoor, Door passengerDoor, Seat driverSeat, Seat passengerSeat); Engine(float dissplacement, int pistonCount); Battery(float voltage); Door(Window window); new Door(new PowerWindow()), new Door(new PowerWindow()), new PowerSeat(), new PowerSeat()PowerWindow() implements Window; PowerSeat() implements Seat; Wheel(Tire tire, Rim rim); ...
Imagine you could just ask for things. Lets start simple and look at the Wheel. The constructor of Wheel needs a Tire and Rim. So when we ask for a Wheel it should be obvious that we want new Wheel(new Tire(), new Rim()). Why do we need to make this explicit in our factory? Lets build a framework from which we can ask for a class and it returns an instance of that class. So in our case if we ask for getInstance(Wheel.class) it returns a new Wheel(new Tire(), new Rim()). Now a framework like this is easy to build since all we need to do is look at the constructor and recursively try to instantiate the objects until all recursive constructors are satisfied.
But things are a bit more complicated than that. What if we ask for Cabin, as in getInstance(Cabin.class)? Well Cabin needs two Doors and two Seats, but Seat is an interface so we have to make a decision: What subclass of Seat should we instantiate? To help our framework make that decision, somewhere we will add a bind method such as bind(Seat.class, PowerSeat.class). Great! Now when we call getInstance(Seat.class) the framework returns new PowerSeat(). Similarly, we will have to call bind(Window.class, PowerWindow.class). Now we can call getInstance(Cabin.class) and the framework will return new Cabin(new Door(new PowerWindow()), new Door(new PowerWindow()), new PowerSeat(), new PowerSeat()).
Notice that closer a class you ask for is to the root (Car in this case), the more work will the framework do for us. So ideally we just want to ask for the root object, Car. Calling getInstance(Car.class) will cause the framework to do all of the work originally in our factory.
As you can see a framework which will call the new operators on your behalf is very useful. This is because you only have to ask for the root object (in our case the Car) and the framework will build the whole object graph on your behalf. This kinds of frameworks are called Automatic Dependency Injection frameworks and there are few of them our there. Namely GUICE, PicoContainer, and Spring.
Since I know most about GUICE, The above example can be rewritten in GUICE like this:
class CarModule extends AbstractModule() { public void bind() { bind(Seat.class, PowerSeat.class); bind(Seat.class, PowerSeat.class); bind(Transmission.class, ManualTransmission.class); // maybe use a provider method?(jwolter) // maybe explain the need for different wheels, so use a Provider; bind(new TypeLiteral>(){}) .toProvider(new Provider>(){ @Inject Provider wp; List get() { return Array.asList(wp.get(), wp.get(), wp.get(), wp.get()); } }); } // or, what I think is simpler and clearer @Provides List provideWheels(Provider wp) { return Array.asList(wp.get(), wp.get() wp.get(), wp.get()); } } // Then somewhere create your application, using the injector only // once, for the root object. Injector injector = Guice.createInjector(new CarModule()); Car car = injector.getInstance(Car.class);
GIMME lol_io LIKE LOLIOSO IM LIKE PROCESSIN WIT DATAZ OK? GIMME EACH BUCKET IN UR DATAZ OK? BUCKET OWN FUBARRED? N CAN HAS NONE NOPE? N CAN HAS 1 KTHXBYE NIZ __name__ KINDA LIKE “__main__”? UR PROCESSIN WIT LOLIO OWN GET_SOME_DATAZ BTW, GET_SOME_DATAZ USES UR INTERNETS LOL
GIMME mock_lol_io LIKE LOLIO BTW, GIMME THING TO TESTBTW, TEST THE THING NOW KTHX
BTW, SOMETIMES THEY BE CALLIN DIS DEPENDENCY INJECTION ROFLBTW, YOU CAN UZE MOCKZ N STUF FER DIS LOOK:IN MAI library GIMME mock_filesystem LIKE LOL_FAKE_FILEYSTEMBTW, NOW U CAN USE LOL_FAKE_FILESYSTEM TO MAKE FAKE FILEZ IN MEMORY N STUFFBTW, IS FASTER THAN OPENIN FILEZ ON TEST SERVAR
public Server buildServer() { Server server = new Server(); SocketConnector socketConnector = new SocketConnector(); socketConnector.setPort(8080); server.addConnector(socketConnector); new ServletBuilder(server) .addServlet("/calc", new CalculatorServlet( new Calculator())) .addServlet("/time", new TimeServlet( new Provider() { public Date get() { return new Date(); } })); return server;}
public Server buildServer() { Server server = new Server(); SocketConnector socketConnector = new SocketConnector(); socketConnector.setPort(8080); server.addConnector(socketConnector); DBConnectionPool pool = new DBConnectionPool(); new ServletBuilder(server) .addServlet("/calc", new CalculatorServlet( pool, new Calculator())) .addServlet("/time", new TimeServlet( pool, new Provider() { public Date get() { return new Date(); } })); return server;}
public static void main(String[] args) throws Exception { // Creation Phase Injector injector = Guice.createInjector( new CalculatorServerModule(args)); Server server = injector.getInstance(Server.class); // Run Phase server.start();}
public static void main(String[] args) throws Exception { // Creation Phase Server server = new ServerFactory(args) .createServer(); // Run Phase server.start();}
const struct {const char* word; bool is_word;} test_data[] = { {"milk", true}, {"centre", false}, {"jklm", false},};TEST(IsWordTest, TestEverything) { for (int i = 0; i < ARRAYSIZE(test_data); i++) EXPECT_EQ(test_data[i].is_word, IsWord(test_data[i].word));}
Locale LOCALES[] = { Word::US, Word::UK, Word::France, ... };const struct {const char* word; bool is_word[NUM_LOCALES];} data[] = { {"milk", {true, true, false, ...}}, // one bool per language {"centre", {false, true, true, ...}}, {"jklm", {false, false, false, ...}}}; TEST(IsWordTest, TestEverything) { for (int i = 0; i < ARRAYSIZE(data); i++) for (int j = 0; j < ARRAYSIZE(LOCALES); j++) EXPECT_EQ(data[i].is_word[j], IsWord(data[i].word, LOCALES[i]));}
TEST(IsWordTest, IsWordInMultipleLocales) { EXPECT_TRUE(IsWord("milk", Word::UK)); EXPECT_TRUE(IsWord("milk", Word::US)); EXPECT_FALSE(IsWord("milk", Word::France));}TEST(IsWordTest, IsWordWithNonExistentWord) { // 'jklm' test is not repeated EXPECT_FALSE(IsWord("jklm", Word::US)); // as it uses the same code path}