Thomas A. Alspaugh
Object Orientation Examples

Under Construction

Javadoc for examples.

What To Look For

This example works through encapsulation, making objects do the acting, and extending the example's world by adding another layer of abstraction, twice. The progression takes us from requirements satisfied dynamically while the program runs, to progressively more requirements satisfied statically (using the structure of the class definitions, which mirrors the structure of the world we are working in), and from an implementation that is fairly tricky to one that is almost boringly straightforward to write, and to review.

Example

Consider a mattress. A good housewife or househusband will extend the comfortable life of a mattress by flipping and rotating it every few weeks so that the strain on the mattress's internal structure is spread around, rather than concentrated in the same areas.

Because a mattress is a rectangular solid, there are only three axes about which it can sensibly be flipped; these axes are shown in Figure 1. The verbs roll, pitch, and yaw are used to describe motions of boats, airplanes, and other objects that can rotate in three-dimensional space.

pitch roll yaw

Figure 1. The three kinds of flips, one around each orthogonal axis

The mattress is drawn with one red end (the head end of the mattress), one blue side (the left side of the mattress), and one surface with a green dot (the top surface of the mattress). We will term the other surfaces the foot, right side, and bottom surface. Of course these names and colors are arbitrary; it doesn't matter what we call them or how we label them, as long as we can keep them straight.

all

Figure 2. All possible flips and orientations
Head, Foot; Left, Right; Top, Bottom

Also because a mattress is a rectangular solid, there are only a limited number of orientations in which a mattress can be placed on its box springs. These orientations are shown in Figure 2; there are four, named for convenience HLT, HRB, FLB, and FRT.

Note that since there are four orientations, there are twelve (4!) ordered pairs of orientations:

  1. (HLT, HRB) connected by a roll
  2. (HLT, FRT) connected by a yaw
  3. (HLT, FLB) connected by a pitch
  4. (HRB, FRT) connected by a pitch
  5. (HRB, FLB) connected by a yaw
  6. (HRB, HLT) connected by a roll
  7. (FRT, FLB) connected by a roll
  8. (FRT, HLT) connected by a yaw
  9. (FRT, HRB) connected by a pitch
  10. (FLB, HLT) connected by a pitch
  11. (FLB, HRB) connected by a yaw
  12. (FLB, FRT) connected by a roll

You'll also note that two pitches in a row leave the mattress in its original orientation, as do two rolls or two yaws. It's also the case that it is possible to visit all four orientations by doing a pitch, a roll, a pitch and a roll; or a roll, a yaw, a roll, and a yaw; or a yaw, a pitch, a yaw, and a pitch.

In the physical world, three-dimensional space constrains a mattress to only move as outlined in the figures and the list. In our model and its implementation, we must constrain a mattress to only move that way with the implementation.

We can model a mattress and its flips and orientations in object oriented fashion. There are several sensible ways to do this, each with its own advantages. Here is the javadoc for the three example ways.

Mattress1: State implemented with several attributes, and the mattress does the flips

Figure 3. Mattress1 world,
consisting only of mattresses

This first way to model a mattress represents a world consisting only of mattresses. In the javadoc, this world is represented by:

Since we are trying to reason consistently, the Mattress1 methods inherit not only the signatures of each interface method (name, parameters, return type) but also its description. See the javadoc for the inherited descriptions.

This design and implementation are conceptually straightforward. The implementation uses three boolean attributes head, left, and top to represent a mattress's state. The values of these attributes are visible through methods that return them, but the values are only set through calls to the pitch, roll, and yaw methods. The implementation of these methods have to maintain the proper relationships among the three attributes.

This design is object oriented: it results in objects that are not passive (their pitch(), roll(), and yaw() methods make the mattress do the flips), that have state, and that encapsulate that state.

Code for HasHLT.java

Code for PRYable.java

Code for Mattress1.java

Mattress2: State implemented with one Orientation object, and the orientation does the flips

Figure 4. Mattress2 world,
consisting of mattresses and orientations

The second way to model a mattress represents a world consisting of mattresses and orientations. There are exactly four orientations in this world, no more, no less; they have no state and they exist all the time (and thus are somewhat like values). Each mattress has an orientation as its state.

Unlike for Mattress1, in this implementation the orientation does the flipping: an orientation has methods for pitch(), roll(), and yaw() that return the new orientation resulting from each of those flips. Thus a mattress delegates flipping to its current orientation, and the mattress takes the new orientation that the old orientation's method returned.

The implementation still maintains the proper relationship among the mattress's head, left, and top, but now instead of being calculated for each flip, the proper relationships are set at compile time. This requirement for the mattress has been turned from a run-time concern into a primarily design-time concern, with the relationships set statically when the four orientations are constructed.

This design is even more object oriented. It has objects that are not passive, not only the mattresses but also the orientations. A mattress's state is even further encapsulated, in another object.

Most significantly, this design takes work that was accomplished by Java code at run time by Mattress1, namely keeping a mattress's state consistent with the physical world, and moves some of it to design time, by embodying each of the four states as a constant, stateless, singleton object of a subclass of Orientation. Now the Java language is ensuring that there are only four states, and doing some of this work at compile time rather than run time.

Code for Mattress2.java

Code for Orientation.java

Mattress3: The flips are objects too; now they flip each other

Figure 5. Mattress3 world,
consisting of mattresses, orientations, and flips

The second way to model a mattress promoted the formerly abstract orientations, which had been embodied in groups of attributes, to be individual objects instead. The third way to model a mattress continues this trend by additionally promoting the three abstract flips (pitch, roll, yaw), which were embodied in the method implementations of Mattress1 and then Orientation, into individual objects.

A flip takes an orientation and returns the next orientation: it is a unary operation on orientations.

Now that flips are objects, we can also consider operations on flips. The appropriate mathematical operation is composition of two flips. We express this with a method

  Flip composeWith(Flip _other) 

that takes a flip _other and returns a composition of the flip with _other. If we call x.flip(y), the result is x.y or the flip that produces the same result as x followed by y. Now instead of performing a sequence of flips of mattress objects, we can figure out what would happen if we did by composing flip objects.

The implementation of Flip uses the run-time type of flips to select the right composition result using Java's type system:

  1. Each subclass S's composeWith(Flip _other) method is implemented identically:
      public Flip composeWith(Flip _other) {
        return _other.composedWith(this);
      } 

    It calls _other's composedWith(S) method, using the type of this (S) to select among _other's four composedWith() methods.

  2. Each subclass's four composedWith() methods have very simple implementations, each one simply returning the appropriate flip. For example, FlipPITCH's composeWith(FlipROLL) method is
      Flip composedWith(FlipROLL  _other) {  return YAW;    } 

    No run-time calculation is needed because a pitch followed by a roll is always the same as a yaw.

As a result, the implementations of the Flip subclasses are very simple, easy to write (once the fairly sophisticated design is done) and easy to check for correctness.

Notice how as we progressed from the initial design, beginning with orientations expresssed as a group of attributes, then expressing the abstract orientations as objects, and finally expressing the abstract flips as objects, more and more of the code has become very very simple. Might one even say there are obviously no deficiencies? We have moved work from run time, where it is more difficult to review and for which testing is needed, to design time (and compile time), where it can be simpler to review, and some of the verification can be done by the compiler (because some of the work is done by the type system).

Code for Mattress3.java

Code for Flip.java

flip bgunflip