Interfaces allow you to use selected functionality of a class without needing to know the concrete type. An interface is perhaps the most powerful form of polymorphism. In Java, interfaces are a built-in type. In C++ interfaces are pure virtual base classes. In COM, interfaces appear as an array of function pointers. The Bridge/Impl pattern is a very effective way to simplify the use of interfaces.
When you require someone else to implement an interface, such as a callback, you should oblige them to implement as few methods as possible. Keep the methods simple and their behavior unambiguous. When you provide interfaces for others to use, they want you to provide as many convenient services as possible. Users want extra methods with default arguments, and they want robust tolerance of special cases. Most interfaces eventually have both uses. How can you avoid this conflict in priorities? You need the Bridge pattern.
Begin with the simplest methods necessary to define your
functionality. For a Vector
you might define methods to
add
another vector and to scale
by a constant. These
methods will constitute your implementation interface, called
Impl
for short. Next define a full-featured interface that
has convenience methods like zero
and copy.
Most often,
you want to derive the full-featured interface from the simple
Impl
interface, so that simple methods are still available.
The extra methods can be coded by using only the methods in the
Impl
class. You can code these implementations once and for
all in one place, the bridge class.
In Java your bridge class might look like
public interface VectorImpl { public void scale(double factor); public void add(Vector anotherVector); } public interface Vector extends VectorImpl { public void zero(); public void copy(Vector anotherVector); } public class VectorBridge implements Vector { private VectorImpl _vectorImpl; public VectorBridge(VectorImpl vectorImpl) { _vectorImpl = vectorImpl; } public void scale(double factor) { _vectorImpl.scale(factor); } public void add(Vector anotherVector) { _vectorImpl.add(anotherVector); } public void zero() { _vectorImpl.scale(0.); } public void copy(Vector anotherVector) { this.zero(); _vectorImpl.add(anotherVector); } }
Most of your code will export Vector
interfaces, and you will
require users to implement VectorImpl.
VectorBridge
makes
a VectorImpl
look like a full-featured Vector.
In C++, you can follow the same style or you can abbreviate with an abstract class. (Some methods have implementations, and others do not.)
class Vector { public: virtual void scale(double factor) = 0; virtual void add(const Vector& anotherVector) = 0; virtual void zero() { this->scale(0.); } virtual void copy(const Vector& anotherVector) { this->zero(); this->add(anotherVector); } virtual ~Vector() {} };
Now we have only one interface for a vector. The three preceding
classes have been combined into one. Users can override the extra
methods if they have a more efficient implementation. Otherwise,
they get a usable default. On the other hand, if you need only
the Impl
methods, a user may unnecessarily optimize unused
methods.
You can code an identical abstract class in Java, but with a serious drawback. Java classes can implement any number of interfaces but can extend only one abstract class. Personally, I prefer to keep all implementation out of interfaces. There are good reasons for avoiding multiple inheritance of implementations. Nevertheless, the simplicity of adding default convenience methods is very tempting.
Bill Harlan, 1999
2009 postscript:
The Bridge is a structural pattern that was originally drawn by Gamma et al in "Design Patterns" (1994, Addison Wesley) with two abstract classes and concrete instances of each. One abstraction was intended for a client, and the other was intended for the implementation. The client abstraction accepted an instance of the implementer and delegated services to that implementer. For me, the best use of this pattern seemed obvious: separate your abstraction of what a client wants from what an implementation finds it easy to provide.
Since then, the Bridge pattern has been drawn many other ways, with different uses and intentions. For this reason, I no longer call this pattern a bridge pattern, but rather an interface decorator. See a more recent description of the approach here: [ Avoid_extending_classes.html ] .
Return to parent directory.