Skip to main content

By clicking Submit, you agree to the developerWorks terms of use.

The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

  • Close [x]

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerworks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

By clicking Submit, you agree to the developerWorks terms of use.

All information submitted is secure.

  • Close [x]

developerWorks Community:

  • Close [x]

In pursuit of code quality: Beware the tight couple!

Use the Dependency Inversion Principle to untangle tightly coupled code

Andrew Glover (aglover@stelligent.com), President, Stelligent Incorporated
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.

Summary:  You know tight coupling is bad news and you really want to avoid it in your designs -- but the question is how. This month, learn how to recognize a tightly coupled system and then disentangle it using the Dependency Inversion Principle.

View more content in this series

Date:  22 May 2007
Level:  Introductory

Activity:  21018 views
Comments:  

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.

Improve your code quality

Don't miss Andrew's code quality discussion forum for assistance with code metrics, test frameworks, and writing quality-focused code.

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.

It's dependency inversion, not dependency injection

The Dependency Inversion Principle is not directly related to dependency injection. Dependency injection, sometimes known as inversion of control (IOC), refers to using a framework like Spring to link object dependencies at run time rather than at compile time. While dependency inversion and dependency injection need not be used together, they are complementary: both techniques strive to utilize abstractions over implementations. See Resources to learn more about the Dependency Inversion Principle.

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

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 {							
       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.

The DAO pattern

Data Access Object (DAO) is a design pattern that aims to separate low-level data access operations from high-level business logic using interfaces and associated implementations. Essentially, a concrete DAO class implements the logic for accessing data from a specific data source. The DAO pattern makes it possible to define multiple concrete implementations for multiple databases, or even varying data sources like file systems, using just one interface.

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

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.


Resources

About the author

Andrew Glover

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.

Report abuse help

Report abuse

Thank you. This entry has been flagged for moderator attention.


Report abuse help

Report abuse

Report abuse submission failed. Please try again later.


developerWorks: Sign in


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

Choose your display name

The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


Rate this article

Comments

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Java technology
ArticleID=224772
ArticleTitle=In pursuit of code quality: Beware the tight couple!
publish-date=05222007
author1-email=aglover@stelligent.com
author1-email-cc=