Interfaces are interesting…

Cedric’s got an interesting post on obtaining extensibility via interfaces As usual, he makes a lot of very good points, and (again, as usual when I link to Cedric’s posts), there are a couple that I think could be elaborated on.

I’d suggest you read Cedric’s post to get the main gist, but here’s a quick summary: interfaces are great for providing extensibility, and you should consider them immutable once published.

I agree totally with Cedric on the first half of this summary. The first rule of OO design for me is “Code to type, not class”[1]. I also agree totally with the second point – but only in the right situation.

Cedric’s post is in response to an article from Artima.com namely an interview with Erich Gamma one of the co-authors of “Design Patterns”, and now the lead architect for Eclipse. Cedric highlights one point from this article for discussion:

Since changing interfaces breaks clients you should consider them as immutable once you’ve published them. As a consequence when adding a new method to an interface you have to do so in a separate interface. In Eclipse we take API stability seriously and for this reason you will find so called I*2 interfaces like IMarkerResolution2 or IWorkbenchPart2 in our APIs.

This is good advice, under the right circumstances. I don’t think Cedric did a very good job of explaining the right circumstances – to be honest, I think the right circumstances are actually relatively rare.

Cedric described how, in COM programming, interfaces are meant to be immutable once published. In COM, interfaces were immutable because adding a method to an interface could break the vtable used during compile-time binding – exactly the same as adding a new method to a class .h header file in C++ could cause client code that wasn’t recompiled to break (Note that dynamically bound interfaces, ala VB, didn’t have this problem). Clients of the interface would sometimes break, or sometimes not – really annoying things to debug, BTW (speaking from experience)[2]. This is not a restriction in Java; you should feel free to add new methods to interfaces if it makes sense. It is not appropriate to look at a technical limitation of COM and declare it to be a good general principle of OO design.

(However, adding a method to an interface often amounts to giving a class a new responsibility; this often is not good. In particular, if adding a method would change the contract of other methods, this would be a bad thing, because this would mean that clients would need to change)

What happens in Java is that the implementers of an interface break; this, while not perfect, is not the same problem as when the users of an interface break. So this suggests one situation where it is important to consider using an immutable interface: when you have interfaces that third-party products (with different lifecycles from the core product) are required to implement. Or, to put it another way, if you don’t have third-party products, or if they have lifecycles tied to your own, you can add methods to an interface safely. This, of course, describes the vast majority of software development – in house or closed product development. Projects like Eclipse, where you build a framework for hordes of external parties to build on, are the minority.

(A compromise, of course, is the interface-with-abstract-class pattern; the interface provides flexibility with some risk of breakage for implementers, whilst the base class provides default behaviour and safety with loss of flexibility. This is used on many open-source projects. Another technique that I’m seeing occasionally is to use dynamic proxies to decorate an interface – this works very well when you only wish to decorate a subset of the interface, and it gives you extensibility with safety as a bonus)

So, an immutable interface is a requirement only when you have third-parties who will implement it. Note that you can still have third-parties that use the interface – if, for example, the contract of the interface says that instances will be created by a factory you control (this means that people who write their own implementation will be violating the contract). Gamma summed this up in the article by invoking Martin Fowler’s distinction between published and public methods – it’s only published interfaces that need to be immutable, whilst interfaces that are merely public should be considered as potentially changeable.

(Curiously, Cedric’s fondness of the ‘I’ prefix is a violation of the code-to-type-not-class rule; it means he can not turn a class into an interface without breaking client code, even though this should be possible and is trivial to do if the Factory pattern has been used. This means he is forced to create interfaces when a class would be sufficient for now, because he’s cut off an otherwise safe evolution path through an artificial naming convention)

When designing an open framework, you need to put more effort into the design to get it right than you do for closed product code. This is not surprising: you need to put more effort into the design of a 747 than you do for a car, and still more effort for the Space Shuttle. The fact that other parties are plugging into your code brings new forces and constraints into play – this changes the rules.

Indeed, without the constraint of multiple lifecycles, forcing developers to publish interfaces runs the risk of getting premature publishing. This leads to the proliferation of very similarly named interfaces, such as ‘java.awt.LayoutManager2’ – a ‘2’ extension to a type name is a shorthand for “We made a mistake, but weren’t able to fix it in time”.

Cedric also made a dig at the XP principle “Do The Simplest Thing That Can Possibly Work”. What Cedric fails to appreciate, I think, is the “can possibly work” clause – code is too simple if it doesn’t satisfy the forces in play. For example, changing a published interface is too simple, because it breaks 3rd-party code. Writing an in-house application or closed-product application has very different constraints to a framework-based application. Of course, this principle is also used by developers to write code that is too simple, because they don’t appreciate those forces themselves. But that’s a different problem.

Publishing interfaces, and keeping them immutable, causes its own constraints. For example, the Hibernate team felt the need to completely repackage their project, so as to avoid breaking client code. I’m not saying that this decision was wrong (it wasn’t), but it’s certainly an indication of what the price of publishing interfaces can be. You certainly don’t want to do this if you don’t need to.

Erich Gamma also pointed out some other ways of obtaining extensibility. One way is the Strategy pattern. So, for example, Cedric’s coffee maker example could be re-done by making the coffee maker interface advertise capabilities – any given implementation could have different capabilities from any other implementation. A strategy, then, can use the capabilities to decide what to do. So, Cedric’s example could look like this:

// This is an implementation of a coffee making strategy
// that adds milk if possible, unless cream is available.
public void dispenseCoffee(CoffeeMaker mrCoffee) {
  mrCoffee.grindBeans();
  if (mrCoffee.supports(CREAM)) {
    mrCoffee.add(CREAM);
  } else if (mrCoffee.supports(MILK)) {
    mrCoffee.add(MILK);
  }
  if (mrCoffee.supports(SUGAR)) {
    mrCoffee.add(SUGAR);
    mrCoffee.add(SUGAR);
  }
  // Hot water last: see http://www.bbc.co.uk/dna/h2g2/B996668
  // it applies to coffee as well as tea.
  mrCoffee.add(HOT_WATER);
}

(You can click here if you want to follow the link)

Cedric’s use of instanceof is actually a way of querying these capabilities, albeit one bound into the object at instantiation time. The capability approach is more flexible – after all, my Mr Coffee machine may have run out of milk (or it may not be fresh… eww). Naturally, CREAM, MILK, SUGAR, and HOT_WATER would all be implementations of an interface as well. However… this is all overkill if you don’t need it, and it’s extra work. Those are engineering trade-offs to consider.

Last, but not least, one commentator, Jem, suggested making two methods that took different parameters, like this:

public void makeCoffee(ICoffeeMaker coffeeMaker) {
  coffeeMaker.pourBeans();
  coffeeMaker.makeCoffee();
}

public void makeCoffee(IMilkCoffeeMaker coffeeMaker) {
  coffeeMaker.pourMilk();
  this.makeCoffee((ICoffeeMaker) coffeeMaker)
}

To Jem, in your example, you’d only be able to use that if you knew it was an IMilkCoffeeMaker at compile time, not run time. In other words, you don’t have true polymorphic behaviour.

In general, however, you’d be better off simply asking the CoffeeMaker to make the coffee for you; this snippet (which, admittedly, is contrived) is a good example of a violation of the Ask-Don’t-Tell principle of OO. So, for example, I could make a Decorator for ICoffeeMaker that would pour the milk when requested. Or I could make an adaptor to adapt existing CoffeeMaker implementers to IMilkCoffeeMaker. Or a few other choices, all without violating the Ask-Don’t-Tell principle. Cedric had a very good point though: the rules are there to make you think before breaking them, not to stop you breaking them.

—-

[1] Another way to put this is “Program to an interface, not an implementation”, which is Gamma’s own words from Design Patterns, as noted in the interview.

[2] The exact circumstance, from memory, is that methods defined in the header before the new method would work okay, but methods defined afterwards would break. This is because the compiler creates a ‘vtable’, which is essentially a list of function pointers, that it accessed via a known index. Add a method anywhere but at the end, and you break your callers. Oh, and if you weren’t writing your header manually, it often got sorted alphabetically (again, from memory)

Advertisements

Author: Robert Watkins

My name is Robert Watkins. I am a software developer and have been for over 18 years now. I currently work for people, but my opinions here are in no way endorsed by them (which is cool; their opinions aren’t endorsed by me either). My main professional interests are in Java development, using Agile methods, with a historical focus on building web based applications. I’m also a Mac-fan and love my iPhone, which I’m currently learning how to code for. I live and work in Brisbane, Australia, but I grew up in the Northern Territory, and still find Brisbane too cold (after 16 years here). I’m married, with two children and one cat. My politics are socialist in tendency, my religious affiliation is atheist (aka “none of the above”), my attitude is condescending and my moral standing is lying down.

2 thoughts on “Interfaces are interesting…”

  1. It took me a minute or two to see what you mean. Cedric’s sample makes a runtime check for an inheritance relationship between ICM and IMCM, whereas I presume the relationship at compile time. It is something I should know at compile time. I compile based on the knowledge of inheritance relationships all the time. Either way, its a dreadfully smelly sample. I like your assertion of the Ask-Don’t-Tell principle.

    As a coffee aficionado I also whole-heartedly approve of the milk-first (water-last) principle. 🙂

  2. Actually, *now* I know what you mean. If the compile-time parameter was of the downcast type ICM, then the IMCM parameter method would not be called. Thanks Rob for setting me straight. 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s