7.5 Relative position

We define the <relative-position> class as follows:

define class <relative-position> (<position>) 
  // distance is in miles
  slot distance :: <single-float>, init-keyword: distance:;
  slot angle :: <relative-angle>, init-keyword: angle:;
end class <relative-position>; 

The distance slot stores the distance to the other object, and the angle slot stores the direction to the other object. Unfortunately, the angle needed here is different from the <directed-angle> class, because the <directed-angle> class has a direction, such as south, which is not needed for the angle of <relative-position>.

We need to provide a class of angle without direction, which we can use for the angle slot of the <relative-position> class). Therefore, we define two new classes, and redefine <directed-angle>:

// Superclass of all angle classes
define abstract class <angle> (<object>)
  slot total-seconds :: <integer>, init-keyword: total-seconds:;
end class <angle>;
define class <relative-angle> (<angle>)
end class <relative-angle>;
define abstract class <directed-angle> (<angle>)
  slot direction :: <string>, init-keyword: direction:;
end class <directed-angle>;

Modularity note: Why provide both the classes <angle> and <relative-angle>, when the <relative-angle> class has no additional slots? We need a class that has only the total-seconds slot, and no others. We need to use such a class as the type of the angle slot of <relative-angle>. We might consider making the <angle> class concrete, and using that class, which has only the total-seconds slot. However, that approach would not prevent someone from storing a <directed-angle> instance in the angle slot of <relative-angle>, because <directed-angle> instances are also instances of <angle>.

In Dylan, by defining classes as specifically as possible, you enhance the reliability of your program, because the compiler (or run-time system) can verify that only correct values are used. In contrast, you could write a program in Dylan or C in which you represented everything as an integer — in that style of program, someone could far too easily introduce a programming error in which a time was stored where a latitude was needed.

The <angle> class looks remarkably similar to the <time> class defined earlier:

// Superclass of all angle classes
define abstract class <angle> (<object>)
  slot total-seconds :: <integer>, init-keyword: total-seconds:;
end class <angle>;
// Superclass of all time classes
define abstract class <time> (<object>)
  slot total-seconds :: <integer>, init-keyword: total-seconds:;
end class <time>; 

We would like to call decode-total-seconds on instances of <angle>, but currently the method is defined to work on <time>. The next step is to take advantage of the similarity between <angle> and <time>.