Interfaces and Abstract Classes
Contents:
1. Introduction:
2. Showing that a class is abstract in the UML:
3. An example of (a) subclassing and (b) interfaces:
4. Lollipop Notation:
5. Components:
6. The lollipop and bowl notation:
7. Example:
8. When to use an interface and when to use an abstract class:
9. Stereotypes:
10. One interface inheriting from another interface:
11. Exercises:
1. Introduction:
A pure interface, as in Java, is a class with no implementation and, therefore, has operation declarations but no method bodies and no fields. In Java the compiler checks that the implementing class provides implementations of all the interface’s operations.
Instead of using the interface construct that
Java provides, interfaces are often declared through
abstract classes. Such abstract classes may
provide some implementation, but often they are used primarily to declare an
interface. The point is that subclassing, or some other mechanism, will provide the implementation, but clients will
never see the implementation, only the interface.
Figure
1
The text editor represented in the diagram above is a typical example of this idea. To allow the editor to be platform independent, we define a platform-independent abstract Window class. This class has no method bodies, just operations; it only defines an interface for the text editor to use. Platform-specific subclasses can be used as desired. Most of the design will be done generically, and only later will there be an implementation provided. This means that if the implementation changes, only a small amount of work will need to be done.
2. Showing that a class is abstract in the UML:
If you have an abstract class or method, the UML convention is to italicise the name of the abstract item. You can use the {abstract} constraint as well (or instead). Some people use {abstract} when handwriting on whiteboards, blackboards or paper because they can’t write italic text well.
3. An example of (a) subclassing and (b) interfaces:
In Figure 2, we see InputStream, DataInput, and DataInputStream
(Defined in the standard java.io
package, a data input stream lets an application read
primitive Java data types from an underlying input stream in a
machine-independent way.
The DataInput
interface provides for reading bytes
from a binary stream and reconstructing from them data in any of the Java
primitive types.).
InputStream is an abstract class; DataInput is an interface.
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 realisation relationship. The symbolism for realisation is deliberately similar to that for generalisation (the closed arrowhead). Realisation indicates that one class implements behaviour specified by another. It is permissible for one implementation class to realise another; this means that the realising class must conform to the interface but need not use inheritance.
An aside: The three class diagram perspectives and their relation to the new stuff taught here:
You'll recall the
three perspectives from our work on class diagrams. They are conceptual,
specification and implementation. Since the conceptual
perspective doesn't talk about software at all, there's no difference between
subclassing an abstract class and realising an interface. Even at specification
perspective, there is no difference.
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 minimised.)
Note, incidentally that Fig 2 show most of the relations that are possible on class diagrams and possibly package diagrams. Here is a table of the complete set. You'll see that you've now met all of them.
The hand-drawn diagrams below shows a summary of two parts of diagram 2 above showing the different notation used for subclassing an abstract class, the first diagram, and realising an interface, the second diagram.
4. Lollipop Notation:
Figure 3 shows an
alternative, more compact notation.
Here, the interfaces are represented by small circles (often called
lollipops) coming off the classes that implements them.
With lollipops,
there is no distinction between realising an interface and subclassing an abstract class. Although the notation is more compact,
you cannot show the operations of the interface or any generalisation
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.
In the hand-drawn diagrams below is a part summary of both of the machine-drawn diagrams (figures 2 and 3) above:
With reference to the hand-drawn diagrams above, the diagrams to the left show subclassing an abstract class and the diagrams on the right, realising an interface. The lower set of diagrams show the simplified version of the top row of diagrams, emphasising that the simplified version loses the distinction between subclassing an abstract class and realising an interface. One might say "So what? It doesn't matter if this distinction is lost. Unless we're working at implementation level, (and most of the time we're not) there is no difference between subclassing an abstract class and realising an interface, so it's an unnecessary detail to show a distinction in your notation. This lack of a distinction is therefore an advantage, not a disadvantage."
5. Components:
Before you study the next section, note that the boxes on the diagram below (Fig 4) relate to components, not classes. Components are regarded as stereotypes of classes. We could have drawn a class box and put the text <<component>> into the box; that would be using the same idea as we did for interfaces. Instead a visual stereotype is used. You can see it in the top right corner of each component rectangle. It's a tiny rectangle, with two even tinier rectangles jutting out of its left side.
Given that components are stereotypes of classes, it follows that they are like classes, but somewhat different. There will be more about components in the section of the notes on physical diagrams. We've not come to that yet.
6. The lollipop and bowl notation:
As well as just having a broken open-arrowed line heading for a lollipop, as shown above, the latest edition of UML had defined a bowl (or uppercase C, if you prefer) and lollipop symbol as well.
The bowl means
"requires an interface"
The lollipop means
"provides an interface".
You'll see two of these to the right of the diagram diagram below, one labelled access control and the other persistence.
There are thus now two pairs of symbols:
A
broken line ending in an arrow and a lollipop means: depends on an interface.
A bowl and a lollipop means: requires an interface.
compared to only the first of the two previously. See the two diagrams at the end of this web page for a good illustration of the symbolism and also the diagram below (Fig. 4).
Figure 4: Diagram shown here purely to illustrate the arrow and lollipop (left side) and lollipop and bowl (right side)
7. Example:
Note that ArrayList implements List. Some other data structure could be substituted as long as it implements the same List interface.
The next diagram is, more or less, the same as the diagram above, except using a socket (a solid line with a cup on the end of it) instead of the dependency arrow at the end of a broken line. This is the more modern notation, referred to already; it's coming into popularity:
In the last paragraph, I wrote more or less. That's because there's some people that perceive the need to retain the old notation in addition to the new. The person who drew Figure 4 above thinks that it is. He sees a subtle difference between the two, which we needn't bother about too much.
8. When to use an interface and when to use an abstract class:
It's generally considered good OO practice to use Java interfaces to specify the "contract" to which you want to be committed.
Use normal Java class inheritance primarily for code reuse purposes, not for defining contracts. In other words, use abstract Java classes when you want to provide some standard base code but want/need to force the users of your class to complete the implementation (that is: you create a skeleton implementation and the sub-classes must flesh it out).
Putting it all another way:
If the derived type claims an IS A KIND OF relationship with the base type, then use an interface. Another way of thinking about this is that the interface defines a CONTRACT.
If the derived type claims a CAN DO relationship with the base type, then use
an abstract class. Another way of thinking about this is that the abstract
class provides some BASE CODE.
It's worth typing into Google the following interface "abstract class" on just one line. A huge number of references will be given.
Despite what's written above, it's hard to get a feel about when to use interfaces and when to use abstract classes. It's something that creeps up on you as you get more experience.
9. Stereotypes:
Stereotypes have nothing to do with interfaces and abstract classes as such, but the topic is being introduced here because an example of a stereotype occurred in this chapter. It was when we drew a box and put the text <<interface>> inside it.
In general, a stereotype is where we extend the UML language to describe new things by taking an existing piece of syntax that's close to what we're trying to describe. We then modify that syntax to provide us with what we want.
To make this clear:
An interface isn't exactly the same thing as an ordinary class but it's close to it. The natural choice then to describe an interface is to choose the symbol for a class and place the stereotype <<interface>> inside it. I mean, it makes more sense choosing the symbol for class, rather than, to be silly, the symbol for, say, a use case or something. After all interfaces aren't remotely like use cases (obviously!), but they are at least a bit like classes.
"Requiring an interface", now represented by a cup on the end of a line, was once represented by using dependency and putting the stereotype <<requires>> adjacent to the dependency line (see bottom right of figure 4 above). Eventually the stereotype got represented visually as a cup on the end of the line, to represent a socket into which a ball fits.
Yet another stereotype is the one referred to above, for component, in Section 5.
Thus it's by the use of stereotypes that UML gets extended. Popular stereotypes often get adopted into the UML at the next revision.
10. One interface inheriting from another interface:
One interface can inherit from other interface as we see in this example.
Complete Diagram:
Note that there are three line symbols on this diagram:
(a) a broken line ended with an open arrow representing dependency.
(b) a broken line ended with a closed white arrow representing implementation
(i.e. provides an interface), the provider being at the un-arrowed end.
(c) a solid line ended with a closed white arrow representing inheritance.
Abbreviated Version - ball and socket notation (otherwise known as lollipop notation)
A socket is represented by a half circle and a ball is represented by a full circle. [diagram badly drawn: The line to the left of the socket extends all the way to Order, as in second diagram of section 7 above.] The ball indicates that an interface is provided and the socket indicates that an interface is required.
11. Exercises:
1. Fruit is a an abstract class that is a generalisation of Apple.
Draw a class diagram to show this.
2. Vegetable is an interface that is realised by Potato.
Draw a class diagram to show this.
3. Why might one argue that it doesn't matter much that with lollipop notation,
there's no difference between subclassing an abstract class and realising an
interface.
4. Write down and describe all the connecting line symbols used on class
diagrams.
5. Describe what is meant by a stereotype.
6. Give two examples of a visual stereotype.