In the past year or so of writing this column, I have introduced many tools and techniques that you can use to improve the quality of your code. I've showed you how to apply code metrics to monitor the attributes of your code base; how to use test frameworks like TestNG, FIT, and Selenium to verify application functionality; and how to use extension frameworks like XMLUnit and StrutsTestCase (and powerful helpers like Cargo and DbUnit) to extend the reach of your testing framework.
While code metrics and developer testing are fundamental to ensuring code quality throughout the development process (like I always say, test early and often!), they are essentially reactive techniques. You test and measure your code to ascertain and quantify its quality, but the code itself has already been written. No matter what you do, you are stuck with the original design.
Of course, there are better and worse ways to design a software system. One of the keys to good design is keeping an eye on maintainability. Poorly designed and implemented systems may be easy to write but they're a real challenge to support. They tend to be dangerously brittle -- meaning that changes to one area of the system will affect other seemingly unrelated areas -- and therefore difficult and time consuming to refactor. Adding developer tests to the code base gives you a map to work from, but the progress itself is still painstakingly slow.
You can improve already written code by refactoring it, but introducing change after the fact is generally expensive. Wouldn't it be easier if the code was just written well to begin with? This month, I'm introducing a proactive technique for ensuring the quality and maintainability of your software systems. The Dependency Inversion Principle has proved essential for writing high-quality, maintainable, and therefore testable code. The basic idea behind dependency inversion is that objects should be dependent upon abstractions and not upon implementations.
You probably have at least heard the term coupling used in the context of object-oriented programming. Coupling refers to the interrelationship(s) of components (or objects) in an application. A loosely coupled application is more modularized than a tightly coupled one. Its components rely on interfaces and abstract classes as opposed to concrete ones, as they would in a tightly coupled system. In a loosely coupled system, components are interrelated using abstractions instead of implementations.
It's easy to grasp the problem of tight coupling when you see it in a diagram. For instance, take a look at Figure 1, which represents a software system whose GUI is coupled to its database:
Figure 1. A tightly coupled system
The GUI's dependence on an implementation rather than an abstraction limits the system. You cannot operate the GUI without having the database up and running. This design doesn't seem so bad from a functional standpoint -- we've been writing apps like this for years and planes aren't falling out of the sky, after all -- but testing it is another story.
The system in Figure 1 makes it hard to isolate programming concerns, which is essential to testing and maintaining every aspect of a system. You'll need a live database properly seeded with lookup data to test the GUI and a properly working GUI to test the data access logic. You could test the front end with TestNG-Abbot (now renamed to FEST), but that still won't tell you anything about the database functionality.
You can see this nefarious coupling in action below in Listing 1. A
particular button for the GUI defines an ActionListener, which communicates directly with the
underlying database via the getOrderStatus
call.
Listing 1. An ActionListener is defined for a button in the GUI
findWidgetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
String value = widgetValue.getText();
if (value == null || value.equals("")) {
dLabel.setText("Please enter a valid widgetID");
} else {
dLabel.setText(getOrderStatus(value));
}
} catch (Exception ex) {
dLabel.setText("Widget doesn't exist in system");
}
}
//more code
});
|
When the GUI's button component is clicked, a particular order's status is retrieved directly from the database, as shown in Listing 2:
Listing 2. The GUI communicates directly with the database via the getOrderStatus method
private String getOrderStatus(String value) {
String retValue = "Widget doesn't exist in system";
Connection con = null;
Statement stmt = null;
ResultSet rs = null;
try {
con = DriverManager.getConnection("jdbc:hsqldb:hsql://127.0.0.1", "sa", "");
stmt = con.createStatement();
rs = stmt.executeQuery("select order.status "
+ "from order, widget where widget.name = "
+ "'" + value + "' "
+ "and widget.id = order.widget_id;");
StringBuffer buff = new StringBuffer();
int x = 0;
while (rs.next()) {
buff.append(++x + ": ");
buff.append(rs.getString(1));
buff.append("\n");
}
if(buff.length() > 0){
retValue = buff.toString();
}else{
retValue = "Widget doesn't exist in system";
}
} catch (SQLException e1) {
e1.printStackTrace();
} finally {
try {rs.close();} catch (Exception e3) {}
try {stmt.close();} catch (Exception e4) {}
try {con.close();} catch (Exception e5) {}
}
return retValue;
}
|
The code in Listing 2 is bogged down especially by the fact that it communicates directly with a hardcoded database via a hardcoded SQL statement. Yeeesh! Can you imagine the challenge of developer testing this GUI and the associated database (which, by the way, could have just as easily been a Web page)? It gets worse when you think about actually modifying the system, given that any change to the database will affect the GUI.
Now let's consider the same system designed with the Dependency Inversion Principle in mind. As you can see in Figure 2, it is possible to decouple the application by adding two components to it: one is an interface and the other is an implementation:
Figure 2. A loosely coupled system
In the application shown in Figure 2, the GUI relies on an abstraction -- a data access object or DAO. The DAO's implementation is directly dependent on the database, but the GUI itself is not entangled. Adding an abstraction in the form of a DAO decouples the database implementation from the GUI implementation. In place of the database, an interface is now coupled to the GUI code. The interface is shown in Listing 3:
Listing 3. WidgetDAO is an abstraction that helps decouple the architecture
public interface WidgetDAO {
public String getOrderStatus(String widget);
//....
}
|
The GUI's ActionListener code references the
interface type WidgetDAO, defined in Listing 3, and not
the actual implementation of that interface. In Listing 4, the GUI's getOrderStatus() method essentially delegates to the
WidgetDAO interface:
Listing 4. The GUI relies on the abstraction, not the database
private String getOrderStatus(String value) {
return dao.getOrderStatus(value);
}
|
The actual implementation of the interface is totally hidden from the GUI because it requests the implementation type from a factory, as shown in Listing 5:
Listing 5. The WidgetDAO implementation is hidden from the GUI
private WidgetDAO dao;
//...
private void initializeDAO() {
this.dao = WidgetDAOFactory.manufacture();
}
|
Note how the code taken from the GUI in Listing 5 references the interface type only -- the implementation of the interface is not used (or imported) anywhere in the GUI. This abstraction of implementation details is key to flexibility: it enables you to swap out the implementation type completely without affecting the GUI.
Also note how the WidgetDAOFactory from
Listing 5 shields the GUI from the details of how the WidgetDAO type is created. That's the responsibility of
the factory, which is shown in Listing 6:
Listing 6. A factory hides implementation details from the GUI
public class WidgetDAOFactory {
public static WidgetDAO manufacture(){
//..
}
}
|
Having the GUI refer data retrieval to an interface type gives you
the flexibility to create different implementations. In this case, a
database holds widget information, so you can create a WidgetDAOImpl class that communicates directly with the
database, shown in Listing 7:
Listing 7. The WidgetDAO type does the dirty work
public class WidgetDAOImpl implements WidgetDAO {
public String getOrderStatus(String value) {
//...
}
}
|
Note that implementation code isn't included in these examples.
That code isn't important -- it's the principle that counts. You shouldn't
really care how the WidgetDAOImpl's getOrderStatus() method works. It could grab the status
from the database or from a file system -- the whole point is, it doesn't
matter to you!
Because the GUI now relies on an abstraction and obtains an implementation of that abstraction from a factory, you can easily create a mock class that isn't coupled to a database or file system. The mock isolates the GUI as shown in Listing 8:
Listing 8. Isolation made easy
public class MockWidgetDAOImpl implements WidgetDAO {
public String getOrderStatus(String value) {
//..
}
}
|
Adding a mock is the final step in designing this system for maintainability. Isolating the GUI from the database and vise versa means you can test the GUI without concern for specific data. You can also test the data access logic without concern for the GUI.
You may not think about it much, but the applications you are designing and building today could easily outlast you. You'll move on to other projects and companies, but your code, like COBOL, will stay behind, possibly even for decades to come.
One thing developers have come to agree on is that well-written code is maintainable, and the Dependency Inversion Principle is a sure way to design for maintainability. Dependency inversion stresses reliance on abstractions over implementations, which creates a great deal of flexibility within a code base. Applying this technique with the help of a DAO, as you've seen this month, ensures that not only will you be able to modify your code base when you need to, but so will other developers.
- Participate in the discussion forum.
- "The Dependency Inversion Principle" (Robert C. Martin): Introduces the Dependency Inversion Principle, including plenty of examples.
- "Advanced DAO programming" (Sean Sullivan, developerWorks, October 2003): Java developer Sean Sullivan discusses three often overlooked aspects of DAO programming: transaction demarcation, exception handling, and logging.
- "The COBOL jigsaw puzzle: Fitting object-oriented and legacy applications together" (E.S. Flint, IBM Systems Journal, Volume 36, Number 1, 1997): An old gem that
explores some of the conundrums of migrating legacy code into a newer system.
- "
In pursuit of code quality: Testing Struts legacy apps" (Andrew Glover, developerWorks, July 2006): Speaking of legacy apps, have you
tried modifying a Struts application lately?
- "
In pursuit of code quality: Automate GUI testing with TestNG-Abbot" (Andrew Glover, developerWorks, February 2007): Get started with the testing framework recently renamed FEST.
- "Dependency injection in Apache Geronimo, Part 1: A new way to look at decoupling in J2EE applications" (Neal Ford, developerWorks, February 2006): Familiarize yourself with dependency injection, using Apache Geronimo as a
working example.
- "Use Inversion of Control in method signatures" (Andre Fachat, developerWorks, January 2007): Learn how to use IOC to decrease the coupling between components and improve performance.
-
In pursuit of code quality series (Andrew Glover,
developerWorks): Learn more about writing quality-focused code.
-
developerWorks: Hundreds of articles about every aspect of Java programming.

Andrew Glover is president of Stelligent Incorporated, which helps companies address software quality with effective developer testing strategies and continuous integration techniques that enable teams to monitor code quality early and often. Check out Andy's blog for a list of his publications.




