Montag, 1. Februar 2010

Dependency Injection - What ist that good for and how do I use it?

Dependency injection has become quite popular in recent years. The basic principle behind it is useful, simple and straightforward. Here we go:

The problem: tightly coupled objects

Let's suppose we want to create a simple calendar application. You want the flexibility of the calendar events either to be stored in a database on a remote server or locally in a text file. The simplified class-structure could look like this:


public interface CalendarEntries {
...
}

public class EntriesInDatabase implements CalendarEntries {
...
}

public class EntriesInLocalFile implements CalendarEntries {
...
}



Now we can implement our Calendar-Class like this:


public class Calendar {
private EntriesInDatabase calendarEntries;
}



or, following the paradigm of "program to an interface, not an implementation" we could use this code:

public class Calendar {
private CalendarEntries calendarEntries = new EntriesInDatabase();
}


Both solutions have the calendar-class tightly coupled to the entries-class. This resulting in the issue that if you want to use entries from another source, you have to change the code of the calendar class!

Why is tight coupling a problem?

Tight coupling is not a problem in general as long as you work on a small project in an environment completely controlled by one developer. As soon as a team is involved or unit testing is necessary, the tight coupling could become a huge issue soon.

Solution 1: constructor based injection

This is so simple that I can hear your "thats all?" moaning already: we just let the constructing object decide, which CalendarEntries implementation to use. The Calendar-class will use whatever it gets at construction time:

public class Calendar {
private CalendarEntries calendarEntries;

public Calendar(CalendarEntries entries) {
this.calendarEntries = entries;
}

....
}


To use the calendar-class we call it like this:

public class TestCalendar {
public static void main(String[] args) {
Calendar myServerCalendar = new Calendar(new EntriesInDatabase());
Calendar myLocalCalendar = new Calendar(new EntriesInLocalFile());
}
}



Solution 2: setter based injection

Instead of directly in the constructor the data source could also be set in a setter-method. The underlying principle is basically the same:

public class Calendar {
private CalendarEntries calendarEntries;

public void setCalendarEntries(CalendarEntries entries) {
this.calendarEntries = entries;
}

....
}


And it would be used like this:

public class TestCalendar {
public static void main(String[] args) {
Calendar myServerCalendar = new Calendar();
myServerCalendar.setCalendarEntries(new EntriesInDatabase());

Calendar myLocalCalendar = new Calendar();
myLocalCalendar.setCalendarEntries(new EntriesInLocalFile());
}
}

Keine Kommentare:

Kommentar veröffentlichen