Summary
In a recent blog post, Martin Fowler explains the difference between header and role interfaces. He proposes that role interfaces are better, since they focus interaction with an object to the methods that are actually used by that object's clients.
Advertisement
In a recent blog post, RoleInterface, Martin Fowler discusses the differences between role and header interfaces:
A role interface is defined by looking at a specific interaction between suppliers and consumers... This contrasts to a header interface, where the supplier will only have a single interface...
The strength of a role interface is that it clearly communicates the actual collaboration between an activity and its successors. Often a class doesn't use all the methods of a class, so it's good to show which ones are actually needed. This can be particularly useful if you need to substitute it later on. A header interface forces you to implement every method, even if you're not going to need them; but with a role interface you only need to implement exactly what's needed.
The disadvantage of a role interface is that it's more effort to come up with, since you need to look at each collaboration to form the role interface. With a header interface all you have to do is duplicate the public methods, no thought needed.
Fowler demonstrates a role interface with an example from project management, with an interface describing an activity or a task:
public interface Activity {
MfDate earliestStart();
MfDate earliestFinish();
MfDate latestFinish();
MfDate latestStart();
}
class ActivityImpl... {
List predecessors() ...
List successors() ...
}
With a role interface... we look at how the collaborating objects are actually used. In this case a successor [task] is only used for its latestStart and a predecessor [task] is only used for its earliestFinish. So as a result we create two interfaces which only have the methods we actually use.
public interface Successor {
MfDate latestStart();
}
public interface Predecessor {
MfDate earliestFinish();
}
class Activity {
List predecessors() ...
List successors() ...
}
According to Fowler, role interfaces result in a cleaner, more precise definition of an object's responsibilities and interactions. That helps, because, according to Fowler:
My belief has always been that you should only use interfaces when you really need substitutability, and if you do need interfaces you should think hard about what the consumer of that interface needs.
What do you think about the finer-grained role interfaces versus the coarser header interfaces? In what design situations would you use header interfaces?
Bad idea. I (service provider) don't want to have to know or care who consumes my services or why. I provide what I provide. I do not want to expose fungible, tactical, situational views of myself. An ATM does not first ask me why I want money. (For all I know, some marketing genius at some bank is developing an ATM UI right this minute that will ask this--no, will try to *deduce* it from environmental factors, usage patterns, etc. God Himself forbid. ;-)
> Bad idea. I (service provider) don't want to have to know > or care who consumes my services or why.
What (I believe) Fowler is advocating at the core of his argument is something that I fully agree with: designing interfaces from the perspective of the consumer, not the provider. The consumer is 'who' will call the methods and know what it needs. A provider can only guess at what the consumers need. If there's any hope of the provider-oriented approach being successful, the designer has to provide every method that he or she thinks might be useful. This approach give us interfaces like java.util.Map where there the vast majority of the methods are completely unnecessary for most purposes and really not essential for the primary purpose of associating keys and values. This means implementors have to fill in a lot of methods that are tangential to the problem at hand. In the case of Map, every implementor must provide an entry set which can be non-trivial for a lot of implementations and at the very least verbose and over complicated.
The biggest problem I see with Fowler's approach in Java is actually a limitation of Java and not the approach. Java lacks a basic way to declare union types. That is if I want to declare a method that takes an Object that is a Predecessor and a Successor, I can't do it without introducing a generic type. I believe this was an oversight in the original design of Java. The concept of interfaces leads naturally to union types. They should have been included long ago and allowing them in generics but not adding them to 'normal' declarations makes no sense to me.
I agree with James Watson. The customer should decide what should be provided by a given component. This is precisely what UML 2.0 introduces with the notion of a required interface versus an offered (more traditinal) one. Unluckily enough, I don't know of any programming language supporting that idea directly, although the idea is quite natural.
IBM is currently working on a component specification (SCA) in which theses two aspects of components are emphasized: what it offers (in the form of one or several (e.g. Role-) interfaces), and what it requires in order to be able to supply its functionality (in the form of one or several required interfaces).
Let's consider a familiar example to illustrate the point. Consider a product like a workflow engine, something quite common nowadays. Most such products integrate a security aspect, most of the time by implementing their own security database or LDAP. It is rarely possible to connect an already existing security module (not only dictionnary) that could alredy be present within the enterprise.
Had the building firm done its job properly, it would have defined a required security interface allowing you (the customer) to plug your own security module. This would not hinder it of offering its own separate security module, with an exposed interface precisely compatible with the required one, that you could even possibly use independently at some other place!
A much cleaner architecture.
Admittedly, this example is at a somehow higher level (component) than the original entry of M. Fowler (class), although the ideas are directly transposible from the one to the other. After all, the interface concept is also at the root of objet-oriented programming.