"Program to an interface and not to an implementation...Purring this more succinctly, you should define the top of any class hierarchy with an abstract class, which implements no methods, but simply defines the methods that class will support. Then, in all of your derived classes you have more freedom to implement these methods as most suits your purposes."
Why does an abstract class at the top of the hierarchy give more freedom to implement methods?
Even if the top level was a concrete class, the derived classes could override and implement the methods with equal freedom.
SCJP 1.4 (93%)<br />SCJD (In progress. It can run, but it can't hide...)
Why does an abstract class at the top of the hierarchy give more freedom to implement methods? Even if the top level was a concrete class, the derived classes could override and implement the methods with equal freedom.
The problem comes when you need to have an implementation class which is both part of your inheritance hierarchy and some other one.
Typically this occurs when dealing with third-party APIs. Imagine I want a class which is an implementation of my NumberCruncher hierarchy (perhaps because I want to re-use data and behaviour from parent classes), but is also part of some other hierarchy (because I want to process it with tools which accept an object of the other hierarchy).
Java only has single inheritance, so a class may only inherit from one concrete parent class. But a class may implement many interfaces.
I see this a lot in testing. I have a base class which is useful for testing (let's call it RecordingMock) which has many utility methods and common settings useful in building a mock object. I need this to be a concrete class, because I need to re-use behaviour and data in child implementations.
For a mock object to work, though, it needs to be substitutable for the "real" object. If the base of the real class hierarchy is a concrete class, then my Mock strategy is just not possible. If the base of the real class hierarchy is an interface, however, making a mock implementation is a trivial exercise.
Luckily the managers of the standard Java APIs are aware of this, so almost all of the base classes of the standard API class hierarchies are actually interfaces. It hasn't always been like this, though. One of the main reasons for the introduction of the Collections API in Java 1.2 was because the previous collection classes were all concrete (Dictionary/Hashtable, Vector, ...) The Standard API guys reworked their software to "program to an interface", and I reckon you should, too.
Frank, doesn't that mean that at the top of the hierarchy there should be an *interface*, not a fully abstract class? With *that* I could agree.
I suspect that that sentence was copied from a C++ book, where fully abstract classes are equivalent to interfaces in Java. Probably an oversight by the author not to adjust it.
The soul is dyed the color of its thoughts. Think only on those things that are in line with your principles and can bear the light of day. The content of your character is your choice. Day by day, what you do is who you become. Your integrity is your destiny - it is the light that guides your way. - Heraclitus
Joined: Jan 07, 1999
Um. Yes. I guess I saw "abstract class" and thought "interface". Maybe I still have a little bit of vestigial C++ in there somewhere.
It might just be my personal style but I don't find that I use abstract classes in the "abstract keyword" sense very often at all. I usually try and make them have a sensible default behaviour if possible.
To an author of classes, a caution not to read this to always require an abstract class or interface. The agile crowd with their "do the simplest thing" and "you ain't gonna need it" and "emergent design" ideas would usually urge you to make something work with a concrete class and write the abstraction only when it becomes necessary to solve a problem. Modern IDEs make such refactoring nearly painless.
It's important to read the guideline "program to interfaces" as a consumer of classes, too. For example when we use library classes ...
This gives your method the flexibility to accept any Collection and not worry about the details of which one a caller uses to fit their needs. Of course that example might not always work ... if you really want to use a method that is only provided by LinkedList the 2nd signature is required.
A good question is never answered. It is not a bolt to be tightened into place but a seed to be planted and to bear more seed toward the hope of greening the landscape of the idea. John Ciardi
Joined: Jan 07, 1999
if you really want to use a method that is only provided by LinkedList the 2nd signature is required.
And, of course, if you need sequential concepts not found in Collection, try List before you get to LinkedList.
Program to an interface, not an implementation Bill Venners: In the introduction of the GoF book, you mention two principles of reusable object-oriented design. The first principle is: "Program to an interface, not an implementation." What's that really mean, and why do it?
Erich Gamma: This principle is really about dependency relationships which have to be carefully managed in a large app. It's easy to add a dependency on a class. It's almost too easy; just add an import statement and modern Java development tools like Eclipse even write this statement for you. Interestingly the inverse isn't that easy and getting rid of an unwanted dependency can be real refactoring work or even worse, block you from reusing the code in another context. For this reason you have to develop with open eyes when it comes to introducing dependencies. This principle tells us that depending on an interface is often beneficial.
Bill Venners: Why?
Erich Gamma nce you depend on interfaces only, you're decoupled from the implementation. That means the implementation can vary, and that's a healthy dependency relationship. For example, for testing purposes you can replace a heavy database implementation with a lighter-weight mock implementation. Fortunately, with today's refactoring support you no longer have to come up with an interface up front. You can distill an interface from a concrete class once you have the full insights into a problem. The intended interface is just one 'extract interface' refactoring away.
So this approach gives you flexibility, but it also separates the really valuable part, the design, from the implementation, which allows clients to be decoupled from the implementation. One question is whether you should always use a Java interfaces for that. An abstract class is good as well. In fact, an abstract class gives you more flexibility when it comes to evolution. You can add new behavior without breaking clients.
Bill Venners: How's that?
Erich Gamma: In Java when you add a new method to an interface, you break all your clients. When you have an abstract class, you can add a new method and provide a default implementation in it. All the clients will continue to work. As always there is a trade-off, an interface gives you freedom with regard to the base class, an abstract class gives you the freedom to add new methods later. It isn't always possible to define an interface in an abstract class, but in the light of evolution you should consider whether an abstract class is sufficient.
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. These interfaces add methods to the base interfaces IMarkerResolution and IWorkbenchPart. Since the additions are done in separate extension interfaces you do not break the clients. However, there is now some burden on the caller in that they need to determine at run- time whether an object implements a particular extension interface.
Another lesson learned is that you should focus not only on developing version one, but to also think about the following versions. This doesn't mean designing in future extensibility, but just keeping in mind that you have to maintain what you produce and try to keep the API stable for a long time. You want to build to last. That's been an important theme of Eclipse development since we started. We have built Eclipse as a platform. We always keep in mind as we design Eclipse that it has to last ten or twenty years. This can be scary at times.
We added support for evolution in the base platform when we started. One example of this is the IAdaptable interface. Classes implementing this interface can be adapted to another interface. This is an example of the Extension Object pattern,.