Mickey Williamson

Program to an Interface

If you’re in the Object Oriented Programming world any length of time, you’re going to hear the design principle, “Program to an interface, not an implementation.” But what exactly does that mean?

First, it can be a bit misleading because the word interface is not used in the sense of the OOP programming structure used to provide additional behavior and functionality to classes. It means program to the supertype, not to subtypes. Let’s take a look at a concrete example.

Let’s create a Storm class which we’ll then extend to the subtypes of SnowStorm and RainStorm. Below is the UML diagram. We have the Storm class with a precipitate() method and then two classes that inherit from Storm.

The Storm, RainStorm, and SnowStorm classes in code:

public class Storm {
    public void precipitate() {}
}

public class RainStorm extends Storm {
    public void precipitate() {
	rain();
    }
	
    public void rain() {}
}

public class SnowStorm extends Storm {
    public void precipitate() {
	snow();
    }
	
    public void snow() {}
}

We might be tempted to do the following when creating RainStorm and SnowStorm objects which is an example of “programming to an implementation”:

RainStorm rainStorm = new RainStorm();
rainStorm.rain();
SnowStorm snowStorm = new SnowStorm();
snowStorm.snow();

A better way would be to “program to an interface (supertype)” by setting the declared type of the variable (rainStorm and snowStorm below) to the supertype (Storm) and setting the object assigned to the variable as an implementation of the supertype (RainStorm or SnowStorm). More succinctly, that is, supertype on the left and subtype on the right of the equals sign.

Storm rainStorm = new RainStorm();
rainStorm.precipitate();
		
Storm snowStorm = new SnowStorm();
snowStorm.precipitate();

By doing it this way, we don’t need to know what type of storm it is at runtime. So if we added a static factory method that returns a subtype, we could still make that storm send down its appropriate precipitation without needing to know what type of storm it actually is while we’re coding it. See the MotherNature class at the end of the snippet below.

public class Storm {
    public void precipitate() {}
	
    public static Storm generateStorm(String type) {
	Storm storm; 
	switch (type) {
	    case "rain":
		storm = new RainStorm();
		break;
	    case "snow":
		storm = new SnowStorm();
		break;	
	    default:
		storm = new Storm();
	}
		
        return storm;
    }
}

public class RainStorm extends Storm {
    public void precipitate() {
	rain();
    }
	
    private void rain() {} // By programming to an interface, this can be changed to private
}

public class SnowStorm extends Storm {
    public void precipitate() {
	snow();
    }
	
    private void snow() {} // By programming to an interface, this can be changed to private
}

public class MotherNature {
   public static void main(String[] args) {
        // We don't even need to know the type of storm.
        // The type can be determined at runtime by what the user passes in.
	Storm storm = Storm.generateStorm(args[0]); 
	storm.precipitate();
    }
}

Hopefully this has demystified what it means to “Program to an interface, not an implementation”.

Leave a Comment