Skip to main content

Section 10.6 Refactoring: Extracting Interfaces

Of course we can create an interface directly. But oftentimes the interface naturally grows out of an existing system. We have build some functionality, and then we realize that some methods of a class should be extracted as an interface. That is when the Extract Interface refactoring can come in handy.
As an example, we will revisit our little grade-reporting program. Let’s recall how we left it in the OOP chapter:
  • The Grade class represents the grade entities. It has methods getCredits and getPoints. Perhaps it should have more, but that’s all we needed at the time.
  • The GradeReader class represents an object that reads grades from some input. We can create it from an actual input string, an InputStream or a Scanner. And it basically provides two methods for us, hasMoreEntries() and processNextGradeRow(), which should actually better be called getNextEntry() or something like that. We won’t change it just yet as I have other plans for it.
  • A Summary class is given a GradeReader object and uses it in its invoke() method to read all the grades it can provide and then produce a result.
  • A Main class is starting the "real" system by creating a GradeReader, creating a Summary introducing them to each other and then calling on Summary to do the hard lifting.
So here is the thing: What does our Summary class really need to know about GradeReader? It simply needs to know that it can ask it for more entries. So it needs to know that it has a hasMoreEntries() method and a processNextGradeRow() method. But it really doesn’t care how the GradeReader goes about doing that. However, just by knowing that it is a GradeReader class, it actually does know all that stuff! I couldn’t for example use some sort of ArrayList instead. And that’s a pity, because it means I can’t test things very well: I can only test the Summary class by going through a GradeReader. We will return to this point in the next section.
So this is our thought then, we would like an interface that I will call for now just Producer. Let’s talk about how to do that with the Extract Interface refactoring.

Subsection 10.6.1 Mechanics of Extract Interface Refactoring

  • Decide on the class from which you would like to extract an interface, and the specific methods you would like to have on that interface.
  • Create a new interface containing those methods.
  • Add implements ... to the class definition line.
  • You will need to make the methods from the interface public where they are implemented in the class.
  • Look for places where the class is used as a type (parameter, local variable and field declarations), and see if you can, and also if you should, change the type to use this interface instead.
With an IDE, you simply trigger the Extract Interface refactoring option and specify the methods you would like to have extracted. We do this to the GradeReader class, and the two methods we wanted to use elsewhere, and end up with the Producer Interface:
public interface Producer {
  boolean hasMoreEntries();
  Grade processNextGradeRow();
}
More importantly, our Summary class has now changed, to use Producer:
private final Producer reader;
private int numCourses = 0;
private double totalPoints = 0.00;

public Summary(Producer reader) {
  this.reader = reader;
}
Hm naming that variable reader seems a bit silly now, but let’s hold on to that, as I do one more change. Looking at my interface, there is nothing special about the Grade values that my interface returns, it could be any type. What I mean by that is that as far as what this interface expresses, I would like it to instead express the idea of "returning elements of some type". I want to use a type parameter for that, like so:
public interface Producer<T> {
    boolean hasMoreEntries();
    T processNextGradeRow();
}
I also need to find the various places where Producer was being used, and replace them with Producer<Grade>
But this all would allow me to use this Producer interface in all sorts of situations, with little change.
What’s that? You are saying that this is really what the Iterator interface basically does already? Yep, you bet! So, hm, we should stop using this Producer interface and start using Iterator. It will just take some renaming, but here is a logical sequence of steps to follow:
  • Start by renaming the two methods, into hasNext and next.
  • Then add Iterator<Grade> to the GradeReader class definition list of interfaces: GradeReader implements Producer<Grade>, Iterator<Grade>
  • Now find usages of Producer and replace them with Iterator, and everything should keep working.
  • Remove the Producer<Grade> from the list of implemented interfacers in GradeReader, then delete the interface file that is no longer used anywhere.
And that’s it! We are now using the established interface for accessing a list of items. Actually we could also consider using a Stream instead, we saw a bit of those earlier. Maybe we’ll come back to that.
Don’t reinvent the wheel! Use existing interfaces and classes when possible.