The following is an exercise in software design intended for teaching purposes. We will consider a class representing a duration of time, expressed in hours, minutes, and seconds. Durations may be positive or negative, and we must be able to add and subtract durations, compare two durations, and increment or decrement a duration by one second. Durations will be displayed in the familiar [-]HH:MM:SS format.
Although this is a relatively simple class, it provides an opportunity to explore an important principle of object-oriented programming: the distinction between what a class does and how it is implemented. In particular, we will see how two different internal representations of exactly the same information can lead to substantially different implementations.
We begin with a requirements specification. This document describes the public interface and the expected behaviour of the class, without specifying how the information must be stored internally. In other words, it describes what the class must do, not how it must do it:
We will then examine two possible designs, both satisfying the same requirements.
The first design, Time_hms, uses a representation that closely resembles how humans normally describe durations. It stores the hours, minutes, and seconds in three separate fields, together with a Boolean variable indicating whether the duration is negative. This makes constructing and displaying durations straightforward, but arithmetic requires us to deal with carrying, borrowing, and the signs of the operands:
Design 1: hours, minutes and seconds
The second design, Time_s, stores the entire duration as a single signed integer representing the total number of seconds. The hours, minutes, and seconds must be calculated whenever they are requested or displayed, but arithmetic and comparisons become ordinary integer operations:
You can examine the C++ implementations of both classes, together with a program demonstrating their use, in the source directory. Notice that the two classes provide the same public operations, even though their implementations are quite different.
Finally, we can compare these two designs, considering the complexity of construction, arithmetic, comparisons, accessors, output, and storage.
The purpose of this exercise is not to develop a production-quality time library. Rather, it is to illustrate how the choice of an internal representation affects the complexity of an implementation. In particular, a representation that is natural and convenient for humans is not necessarily the representation that is most convenient for computation. By keeping the public interface independent of the internal representation, we can change the implementation without requiring changes to the programs that use the class.