Interfaces and Abstract Classes

Interfaces and Abstract Classes

One of the great qualities of object-oriented development is that you can vary the interfaces of classes independent of the implementation. Much of the power of object development comes from this property. However, few people make good use of it.

Programming languages use a single construct, the class, which contains both interface and implementation. When you subclass, you inherit both. Using the interface as a separate construct is rarely used, which is a shame.

A pure interface, as in Java, is a class with no implementation and, therefore, has operation declarations but no method bodies and no fields. Interfaces are often declared through abstract classes. Such classes may provide some implementation, but often they are used primarily to declare an interface. The point is that subclassingor some other mechanismwill provide the implementation, but clients will never see the implementation, only the interface.

The text editor represented in Figure 6-10 is a typical example of this. To allow the editor to be platform-independent, we define a platform-independent abstract Window class. This class has no method bodies; it only defines an interface for the text editor to use. Platform-specific subclasses can be used as desired.

Figure 6-10. Window as Abstract Class

If you have an abstract class or method, the UML convention is to italicize the name of the abstract item. You can use the {abstract} constraint, as well (or instead). I use {abstract} on whiteboards because I can't write italic text. With a diagramming tool, however, I prefer the elegance of italics.

Subclassing is not the only way to do this, however. Java provides an interface construct, and the compiler checks that the implementing class provides implementations of all of the interface's operations

In Figure 6-11, we see InputStream, DataInput, and DataInputStream (defined in the standard package). InputStream is an abstract class; DataInput is an interface.

Figure 6-11. Interfaces and Abstract Class: An Example from Java

Some client class, say, OrderReader, needs to use DataInput's functionality. The DataInputStream class implements both the DataInput and InputStream interfaces and is a subclass of the latter.

The link between DataInputStream and DataInput is a realization relationship. Realization is deliberately similar to generalization; it indicates that one class implements behavior specified by another. It is permissible for one implementation class to realize another; this means that the realizing class must conform to the interface, but need not use inheritance.

In a specification model, there is no difference between realization and subtyping.

The link between OrderReader and DataInput is a dependency. In this case, the dependency indicates that if the DataInput interface changes, the OrderReader may also have to change. One of the aims of development is to keep dependencies to a minimum so that the effects of changes are minimized. (There's more on dependencies in Chapter 7.)

Figure 6-12 shows an alternative, more compact notation. Here, the interfaces are represented by small circles (often called lollipops) coming off the classes that implement them.

Figure 6-12. Lollipop Notation for Interfaces

With lollipops, there is no distinction between realizing an interface and subclassing an abstract class. Although the notation is more compact, you cannot show the operations of the interface or any generalization relationships between interfaces.

Abstract classes and interfaces are similar, but there is a difference. Both allow you to define an interface and defer its implementation until later. However, the abstract class allows you to add implementation of some of the methods; an interface forces you to defer definition of all methods.