Newsletter sign-up
View all newsletters

Enterprise Java Newsletter
Stay up to date on the latest tutorials and Java community news posted on JavaWorld

Sponsored Links

Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs

Smarter Java development

Use interfaces to effectively enforce development contracts while maintaining loose coupling of code

  • Print
  • Feedback

Page 2 of 4

Basic example

To further explain the concept of coding to interfaces, I have created a simple example. While this example is clearly trivial, it demonstrates some of the benefits mentioned above.

Consider the simple example of a class Car that implements interface Vehicle. Interface Vehicle has a single method called start(). Class Car will implement the interface by providing a start() method. Other functionality in the Car class has been left out for the sake of clarity.

interface Vehicle {
        // All  vehicle implementations must implement the start method
   public void start();
}
class Car implements Vehicle{
        //  Required to implement Vehicle
   public void start(){ ... }
}


Having laid the foundations of the Car object, we can create another object called Valet. It is the Valet's job to start the Car and bring it to the restaurant patron. The Valet object can be written without interfaces, as follows:

class Valet {
   public Car getCar( Car c){ ... }
}


The Valet object has a method called getCar that returns a Car object. This code example satisfies the functional requirements of the system, but it forever links the Valet object with that of the Car. In this situation, the two objects are said to be tightly coupled. The Valet object requires knowledge of the Car object and has access to all public methods and variables contained within that object. It is best to avoid such tight coupling of code because it increases dependencies and reduces flexibility.

To code the Valet object using interfaces, the following implementation could be used:

class Valet{
   public Vehicle getVehicle( Vehicle c) {  ...  }
}


While the code changes are fairly minor -- changing the references from Car to Vehicle -- the effects on the development cycle are considerable. Using the second implementation, the Valet has knowledge only of the methods and variables defined in the Vehicle interface. Any other public methods and data contained in the specific implementation of the Vehicle interface are hidden from the user of the Vehicle object.

This simple code change has ensured the proper concealment of information and implementation from other objects, and has therefore eliminated the possibility that developers will use undesirable methods.

Creating the interface object

The last issue to discuss with respect to this development technique is the creation of the interface objects. While it is possible to create a new instance of a class using the new operator, it is not possible to directly create an instance of an interface. In order to create an interface implementation, you must instantiate the object and cast it to the desired interface. Therefore, the developer who owns the object code can be responsible for both creating the instance of the object and performing the casting.

This creation process can be achieved using a Factory pattern in which an external object calls a static createXYZ() method on a Factory and returns an interface. It can also be achieved if a developer calls a method on another object and passes it an interface instead of the actual class. This would be analogous to passing an Enumeration interface instead of a Vector or Hashtable.

Detailed example

In order to demonstrate the use of this scheme on a larger project, I have created the example of a meeting scheduler. This scheduler has three major components: the resources (conference room and meeting attendee), the occurrence (the meeting itself) and the scheduler (the one who maintains the resource calendar).

Let's assume these three components were to be developed by three different developers. The goal of each developer should be to establish the usage of his or her component and publish it to the other developers on the project.

Consider the example of a Person. A Person may implement numerous methods but will implement the Resource interface for this application. I have created the Resource interface with all the necessary accessor methods for all resources used in this example (shown below):

public interface Resource {
  public String getID();
  public String getName(); 
  
  public void addOccurrence( Occurrence o);
}


At this point, the developer of the Person functionality has published the interface by which all users can access the information stored in the Person object. Coding to the interface helps ensure that no developers are using the Person object in an incorrect manner. The developer of the Scheduler object can now use the methods contained in the Resource interface to access the information and functionality necessary to create and maintain the schedule of the Person object.

The Occurrence interface contains methods necessary for the scheduling of an Occurrence. This can be a conference, travel plan, or any other scheduling event. The Occurrence interface is shown below:

public interface Occurrence {
   
    public void setEndDatetime(Date d);
    public Date getEndDatetime();  
    public void setStartDatetime(Date d);
    public Date getStartDatetime();
    public void setDescription(String description);
    public String getDescription();
    
    public void addResource(Resource r);
    public Resource[] getResources();
    public boolean occursOn( Date d);
}


The Scheduler code uses the Resource interface and the Occurrence interface to maintain the schedule of a resource. Notice that the Scheduler does not have any knowledge of the entity for which it is maintaining the schedule:

public class Scheduler implements Schedule{
    Vector schedule = null;
    public Scheduler(){
       schedule = new Vector();
    }
    
public void addOccurrence(Occurrence o){
       schedule.addElement(o);
    }
    
        public void removeOccurrence(Occurrence o){
       schedule.removeElement(o);
    }
    
        public Occurrence getOccurrence(Date d)    {
       Enumeration scheduleElements = schedule.elements();
       
       Occurrence o = null;
       
       while ( scheduleElements.hasMoreElements() )  {
          o = (Occurrence) scheduleElements.nextElement();
        // For this simple example, the occurrence matches if 
        // the datetime isthe meeting start  time.  This logic
        // can be made more complex as required.
          if ( o.getStartDatetime() == d) {
             break;
          }
       }
       
       return o;
    }
}


This example shows the power of interfaces in the development phases of a system. Each of the subsystems has knowledge only of the interface through which it must communicate -- no knowledge of the implementation is required. If each of the building blocks in the above example were to be further developed by teams of developers, their efforts would be simplified due to the enforcement of these interface contracts.

  • Print
  • Feedback

Resources