In pursuit of code quality

Beware the tight couple!

Use the Dependency Inversion Principle to untangle tightly coupled code


Content series:

This content is part # of # in the series: In pursuit of code quality

Stay tuned for additional content in this series.

This content is part of the series:In pursuit of code quality

Stay tuned for additional content in this series.

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.

The too-tight couple

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
A tightly coupled system
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.

Did you say 'brittle'?

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 {							
  } 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://", "sa", "");
  stmt = con.createStatement();
  rs = stmt.executeQuery("select order.status "
     + "from order, widget where = " 
     + "'" + value + "' "
     + "and = order.widget_id;");
  StringBuffer buff = new StringBuffer();
  int x = 0;
  while ( {
  buff.append(++x + ": ");
  if(buff.length() > 0){
    retValue = buff.toString();
    retValue = "Widget doesn't exist in system";
 } catch (SQLException e1) {
 } 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.

Turn me loose!

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
A loosely coupled system
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();

Evolution is easy

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!

Now, isolate the GUI

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.

In conclusion

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.

Downloadable resources

Related topics


Sign in or register to add and subscribe to comments.

Zone=Java development
ArticleTitle=In pursuit of code quality: Beware the tight couple!