Object creation is part of the lifecycle management concern. This is typically a responsability which you want to treat separately. Common patterns are:
- dependency injection
- builder, abstract factory or factory method
- singleton, prototype or object pool
To read up on these see for example Google results for creational patterns and dependency injection.
I will focus here on dependency injection because it is the simplest approach matching your example and results in clean, testable code. Other favorite patterns are builder or factory.
Dependency injection (or Inversion of Control) is a very common pattern (although not usually called a pattern, I think it is one). The best know frameworks offering D.I. are Spring or Java's own CDI but you can also do it by hand as shown below.
Constructor Injection
In your example a simple use of dependency injection could work like this:
public Class Dependency {
}
public Class Application {
private final Dependency dependency;
public Application (Dependency dependency) {
this.dependency = dependency;
}
}
Usage:
public static void main(String[] args) {
Application application = new Application(new Dependency());
}
Note that Application
is not creating Dependency
but expects an instance of Dependency
to be supplied to it via the constructor. This is called constructor injection. The advantage of this way is that you can make the dependency field final. A drawback is that, with many dependencies, the constructor might get too many arguments to remain understandable. To avoid that you could use the builder pattern, or use setter injection:
Setter Injection
public Class Dependency {
public String doStuff(String input) {
return input + " stuffed";
}
}
public Class Application {
private Dependency dependency;
public void setDependency(Dependency dependency) {
this.dependency = dependency;
}
public String letsGo(String input) {
String stuff = dependency.doStuff(input);
return "I called my dependency and it said: " + stuff ;
}
}
Usage:
public static void main(String[] args) {
Application application = new Application();
application.setDependency(new Dependency());
System.our.println(application.letsGo("rabbit"));
}
Output:
I called my dependency and it said: rabbit stuffed
Testing
Now about testability. Remember, in a unit test I want to be able to test the class Application
without testing the class Dependency
. If Application
was creating its own instance of Dependency
this would be hard to do. With dependency injection it's easy. Here's an example using JUnit and Mockito with annotations, which is pretty standard:
@RunWith(MockitoJUnitRunner.class)
public class ApplicationTest {
@InjectMocks
private Application instance;
@Mock
private Dependency dependency;
@Test
public void test() {
// SETUP
String testStuff = "some test stuff";
String input = "Micky";
when(dependency.doStuff(input)).thenReturn(testStuff);
// CALL
String actualResult = instance.letsGo(input);
// VERIFY
verify(dependency).doStuff(input);
String expectedResult = "I called my dependency and it said: " + testStuff;
assertEquals(actualResult, expectedResult);
}
}
Here Mockito is creating an instance of Application
for you and injects any fields annotated with @Mock
into it. It automatically determines to do so via the constructor or a setter. The line:
when(dependency.doStuff(input)).thenReturn(testStuff);
Instructs the mock Dependency
to return a specific value when called. This means we are not calling the real Dependency
code but asking Mockito to generate a fake result from that class when it is called.
After calling the Application
instance you can check if the expected call to the dependency was made as follows:
verify(dependency).doStuff(input);
Mockito checks if the specified call was made exactly once and generates an error if this is not the case.
Then using assertEquals
the actual result is compared to the expected result. If the values are not equal, an error is generated by JUnit when you run the test.
So the main lesson from all this is that object creation is an important concern that should ideally be treated separately using one of the creational patterns or dependency injection. By doing this you can focus your class on doing its main task making it easier to understand and test.
Remarks
D.I. itself is probably not considered a pattern because it is not specific enough - you can architect it in several ways. Still, whenever you see code creating instances of its own dependencies you should consider that a code smell and try to refactor it.
The Spring implementation of D.I. uses an abstract factory called the application context. Spring supports injection by simply annotating fields with @Autowired
(or @Inject
):
@Autowired
private Dependency dependency;
The Dependency
instance is obtained by Spring from the application context which is usually an XML file or a class annotated with @Configuration
. In the application context you can specify what dependencies (beans) exist and how they should be set up and wired together.