Abstract Classes and Overriding Make

Abstract classes define the interface, not the implementation, of an object. There are no direct instances of an abstract class. Concrete classes actually implement their interfaces. Every abstract class will typically have one or more concrete subclasses. For example, if plain vanilla vehicles shouldn't exist, <vehicle> could be defined as follows:

define abstract class <vehicle> (<object>)
  // ...as before

The above modification prevents the creation of direct instances of <vehicle>. At the moment, calling make on this class would result in an error. However, a programmer could add a method to make which allowed the intelligent creation of vehicles based on some criteria, thus making <vehicle> an instantiable abstract class:

define method make(class == <vehicle>,
  #rest keys, #key big? (#f), #all-keys)
  => <vehicle>;

  if ( big? )
    make( <truck>, keys, tons: 2 );
    make( <car>, keys );

A number of new features appear in the parameter list. The expression “class == <vehicle>” specifies a singleton, one particular object of a class which gets treated as a special case. Singletons are discussed in the chapter on Multiple Dispatch. The use of #rest, #key and #all-keys in the same parameter list accepts any and all keywords, binds one of them to big? and places all of them into the variable keys. The new make method could be invoked in any of the following fashions:

let x = 1000000;
make(<vehicle>, sn: x, big?: #f); =>car
make(<vehicle>, sn: x, big?: #t); =>truck
make(<vehicle>, sn: x);           =>car

Methods added to make don't actually need to create new objects. Dylan officially allows them to return existing objects. This can be used to manage lightweight shared objects, such as the “flyweights” described by Gamma, et al., in Design Patterns .