If your object needs to instantiate a class, but you don't want it to depend on the details of instantiating the class, inject a factory (as you've suggested).
I like to use interfaces so I can plug in different implementations of my dependencies. Here's an example:
public class RealLoginController implements LoginController {
private LoginViewFactory viewFactory;
public LoginController(LoginViewFactory viewFactory) {
this.viewFactory = viewFactory;
}
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) {
if (isLoggedIn()) {
return viewFactory.createLoggedInView();
} else {
return viewFactory.createLoggedOutView();
}
}
// ...
}
public class RealLoggedInView implements LoginView {
// Implementation for rendering stuff
}
public class RealLoggedOutView implements LoginView {
// Implementation for rendering stuff
}
public interface LoginViewFactory {
public LoginView createLoggedInView();
public LoginView createLoggedInView();
}
public class RealLoginViewFactory implements LoginViewFactory {
private FooModel fooEngine;
private BarConfig barConfig;
public RealLoginViewFactory(FooModel fooLayer, BarConfig barConfig) {
this.fooEngine = fooEngine;
this.barConfig = barConfig;
}
public LoginView createLoggedInView() {
if (fooEngine.hasBaz()) {
return new RealLoginView(barCongig.getQux());
} else {
return new RealLoginView(barCongig.getQux(),
fooEngine.getBaz());
}
}
public LoginView createLoggedOutView() {
// ...
}
}
public class RealLoginController implements LoginController {
private LoginViewFactory viewFactory;
public LoginController(LoginViewFactory viewFactory) {
this.viewFactory = viewFactory;
}
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) {
if (isLoggedIn()) {
return viewFactory.createLoggedInView();
} else {
return viewFactory.createLoggedOutView();
}
}
// ...
}
You can then use the controller with any views you like:
public class LoginControllerTest {
public void testSomething() {
// ...
controller = new RealLoginController(fakeViewFactory);
assertHasTag("td#row1", controller.getRenderedStuff());
// ...
}
}
You may be able to avoid the problem (as bpappa suggests) if you don't need complex instantiation logic and your framework knows how to get dependencies for you by name.