Random musings from my awakening dementia...
09.25.2004  
Loose Coupling, Part 3
 

I'm quite interested in the concept of software components and how those ideas can be applied to Java code. Thoughts or ideas I have on this subject get dropped here for the benefit of humanity and my own hubris.

© 2004-2005, Howard Abrams



Except where otherwise noted, all original content is licensed under a Creative Commons License.
See details.

I know when I promised My Readership some ideas on the subject of loosely coupling components, I thought that by now I would have been running off into the weeds of Reflection or the heights of Aspects. But there are still many more, simpler ways for two components to talk without having too much interdependencies.

Many of these revolve around one or more design patterns, and I thought I would briefly list some of these with a view towards loose component coupling. If you want a good treatise of these design patterns and their use in more general cases, I suggest checking out David Geary’s series for JavaWorld, Java Design Patterns.

I’ve already mentioned the use of the Factory design pattern. The idea here is that you can get an instantiated object without specifying its exact name. A great example of this is found in the Apache Jakarta’s project, Logging Commons, which allows you to write logging code without actually specifying which logging engine you are going to use. You’re code would look like this:

Log log = LogFactory.getLog("myClassName");

The LogFactory.getLog() method will search for a logging engine based on some defined parameters or algorithm. That is, if Log4J is available, it will use that, otherwise, if you are using JDK 1.4, then it will use its logging engine, etc. You’re code now doesn’t have to be dependent on any particular logging engine.

However, you are dependent on this Jakarta library.

In your own code, however, you state that a factory will return an object that implements a particular interface. Which class your factory uses is for you to determine. You can have the factory accept a string description, or read a configuration file, or, in the case of my Ant Task, you can use Ant’s build file.

Another example is JDBC, where you write your program to access a database but not a specific database. You get a JDBC database driver based on a string, not the name of a particular class. This allows you (supposedly) the ability to add a JDBC driver, change a configuration file and your app now uses a different database. Too bad this doesn’t happen as easily in real life, but it is a lot better than the equivalent solution in… say, PHP.

Decorator / Wrapper

Take, for instance, the Decorator design pattern. The Gang of Four define this pattern as:

Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extended functionality.

But don’t let that terse statement throw you off. You’ve probably used this pattern before as this pattern comes in many flavors. Look at the way the Java I/O streams library is implemented:

StreamTokenizer s = 
    new StreamTokenizer (
        new BufferedReader (
            new FileReader (file)));

Since just about every class in java.io takes a Reader or Writer (i.e. a class that implements one of those interfaces) in its constructor, you can easily graft additional functionality to a stream by layering another wrapper over it. In this case, we start with a class that can read a file, and then wrap it with a stream that can read it in buffers, and then we wrap that in a stream class that can tokenize the input.

Often, we are used to creating a parent class and then adding features by subclassing the parent class and overriding one or more methods. This is not always ideal:

  • In Java, you can only derive from one class, so you don’t want to always waste that single silver bullet.

  • You may not have enough knowledge of the guts of the parent class (or the source) in order to correctly override the methods (which may include some hidden side-effects).

But the primary reason for creating a library based on this pattern is simply to limit the number of classes. In the case of the java.io library, they could have created a TokenizingBufferedFileReader class, but creating so many combinations would be mind-numbing. It is best to simply loosely couple them and allow the user to layer the each class to provide the needed features.

Another similar approach to this Decorator pattern is a “plugin” method, where a class is given additional functionality from another class. Like passing a FilenameFilter to the File.list() method.

Of course, the classes must be architected with this sort of extension from the beginning. David Geary, in his article, Decorate Your Java Code, uses the Swing Table as an example of a class that can be extended by passing in a second class that implements an interface. The original Table class then calls a method in the passed in class.

This is the approach I use in my Ant Task, where I can change how it parses and interprets input files by creating and specifying an “engine” through a simple Java class.