Bliki: RequiredInterface

A required interface is an interface that is defined by the
client of an interaction that specifies what a supplier component needs to do so
that it can be used in that interaction.



A good example of required interface is an interface commonly
referred to as “comparable”. Such an interface is usually required
by a sort function. Imagine I have a set of albums, and I want to
sort them by title, but ignoring articles such as "The", "A", and
"An". I can arrange them to be sorted in this way by implementing
the required interface for any sort functions.





In Java it would look
something like this.



class Album...



public class Album implements Comparable<Album> {
private String title;

public Album(String title) {
this.title = title;
}
public String getTitle() {
return title;
}

@Override
public int compareTo(Album o) {
return this.sortKey().compareTo(o.sortKey());
}
private String sortKey() {
return ignoreSortPrefixes(title).toLowerCase();
}
private static String ignoreSortPrefixes(String arg) {
final String[] prefixes = {"an", "a", "the"};
return Arrays.stream(prefixes)
.map(s -> s + " ")
.filter(s -> arg.toLowerCase().startsWith(s))
.findFirst()
.map(s -> arg.substring(s.length(), arg.length()))
.orElse(arg)
;
}

In this case Comparable is the required interface of
the various Java sort functions. More complicated examples can have
a richer interface with several methods defined on it.



Often people think about interfaces as a decision by the supplier
about what to expose to clients. But required interfaces are
specified (and often defined) by the client. You often get more
useful interfaces by thinking about what clients require - leading
towards thinking about RoleInterfaces.





Using an Adapter

A common problem comes up if I want to plug together two modules
that have been defined independently. Here we can run into
difficulties even if we get names that match.



Consider a task list with a required interface of tasks.



class TaskList...



private List<Task> tasks;
private LocalDate deadline;
public LocalDate latestStart() {
return deadline.minusDays(tasks.stream().mapToInt(t -> t.shortestLength()).sum());
}
}


interface Task…



int shortestLength();


Let's imagine I want to integrate it with an Activity
class I got from a different supplier.



class Activity…



public int shortestLength() {



Even though the activity has a method whose signature happens to match the
required interface's, I (rightly) can't create a
task list of activities because the type definitions don't match. If
I can't modify the activity class I need to use an adapter.



public class ActivityAdapter implements Task {
private Activity activity;

public ActivityAdapter(Activity activity) {
this.activity = activity;
}
@Override
public int shortestLength() {
return activity.shortestLength();
}
}


In the software world we use the term adapter pretty freely, but
here I'm using strictly in the sense of the Gang of
Four book
. In this usage an adapter is an object that maps one
object to the required interface of another.





In this case, I don't need an adapter if I'm using a dynamic
language, but I do if the activity class used a method with a
different signature.





Acknowledgements

Alexander Zagniotov and Bruno Trecenti commented on
drafts of this post.




Share:


if you found this article useful, please share it. I appreciate the feedback and encouragement
 •  0 comments  •  flag
Share on Twitter
Published on October 12, 2015 06:16
No comments have been added yet.


Martin Fowler's Blog

Martin Fowler
Martin Fowler isn't a Goodreads Author (yet), but they do have a blog, so here are some recent posts imported from their feed.
Follow Martin Fowler's blog with rss.