Monday, November 10, 2008

Interfaces in Java

Being a busy developer, I'm usually looking for ways of creating code that is clean, fast and re-usable.  In my early coding you'll see me do this in some neat, albeit complex classes that are very specific the the implementation of the day. 

This article deals with a problem I typically run into where developers create code that is so tightly integrated, that if you change a critical piece, the code becomes useless and you have to start all over again.  Java has a way of allowing developers to create good software and be free to implement however they see fit without pigeon-holing the resulting software.

What is an Interface?

Interfaces are like stripped down classes that don't actually perform any functions, they just define what the function will be.  Essentially, an interface defines a 'type' for a class without suggesting any implementation, but sets out the rules for how all of its subclasses will behave.  Take for instance the following interface called Bicycle:

package mobile;
public interface Bicycle {
    void applyBrake();    
    void speedUp();    
    void slowDown();
}

This simple interface defines what things our Bicycle implementations can do, it makes no attempt to define how any of these functions are to be done.


Interfaces have to be implemented so that they can be used in programs.  An interface can never be instantiated for the simple reason that it is not a class.  Think of it as a definition for a structure...like a blueprint.  To implement our Bicycle, we create our MountainBike class:

package mobile;
public class MountainBike implements Bicycle {    
    public void applyBrake() {        
        // TODO Auto-generated method stub    
    }    
    public void slowDown() {        
        // TODO Auto-generated method stub    
    }    
    
    public void speedUp() {        
        // TODO Auto-generated method stub    
    }
}

As you can see, our MountainBike is basically a Bicycle.  Eventually, all the complicated code that makes the MountainBike special will be added by the developer with all the great MountainBike ideas.


When Do You Use an Interface?


The short answer is that interfaces are used for integration.  When you build a program, you want to build components that can be re-used both throughout your program, and with any luck, in your next project.


Use an interface any time you want to use a component but not necessarily be bound to any specific implementation.  Take for instance the following triathlon program:

import mobile.*;
public class Triathlon {    
   public static void main(String[] args) {        
      Bicycle bicycle = new MountainBike();        
      bicycle.speedUp();    
   }
}

This example shows that we declare a Bicycle that is a new MountainBike.  Since there is no requirement that the triathon use a MountainBike, we can declare it for now, and then when we implement a new SuperBike we can then replace the MountainBike without causing us to have to re-define the triathlon.


What's the Point?


At first glance to a lot of programmers, this may seem like extra work by requiring that I create a Bicycle before I dive in and create my MountainBike.  To be honest, if you are not going to take the time to design your program, it is extra, and you should just go ahead and create your program in one big, long, complicated script.  Software design is what allows us to get something working very fast and allow us the flexibility to enhance, invent and adapt later on.


By using interfaces, you can go ahead and design your software without having to work out all the details of how each component will work up front.  You can bring a working prototype to the table quicker and then improve on the pieces later.


Another advantage is that you can adapt existing software to work within a new design.  Take for instance we have a legacy TenSpeed bike that we want to use in our triathlon program:

package legacy;
public class TenSpeed {    
   public void shiftGears(boolean up){        
      // shift    
   }        
   public void squeezeHandBrake(){        
      // brake    
   }        
   public void pedal(){        
      // pedal    
   }
}

As you can see, the TenSpeed won't work in our triathlon without either changing the triathlon or the TenSpeed.  To avoid re-writing either, we can just implement our Bicycle interface on the TenSpeed:

package legacy;
import mobile.Bicycle;
public class TenSpeed implements Bicycle {    
    public void shiftGears(boolean up){        
       // shift    
    }        
    public void squeezeHandBrake(){        
       // brake    
    }        
    public void pedal(){        
       // pedal    
    }    
    public void applyBrake() {        
       squeezeHandBrake();    
    }    
    public void slowDown() {        
       shiftGears(false);    
    }
    public void speedUp() {
       shiftGears(true);
       pedal();
    }
}

With no impact to the legacy TenSpeed functions, I can now integrate my TenSpeed into my triathlon:

import mobile.*;
import legacy.*;
public class Triathlon {
    public static void main(String[] args) {
        Bicycle bicycle = new TenSpeed();
        bicycle.speedUp();
    }
}

No comments: