17.2.2 Vehicle containers

The airport-classes.dylan file. (continued)

// VEHICLE STORAGE
// The default size for a vehicle container
define constant $default-capacity
  = make(<size>, length: 350, width: 200, height: 100); 

// This class represents a location where an aircraft could be stored
define abstract class <vehicle-storage> (<physical-object>)
  slot storage-capacity :: <size> = $default-capacity,
    init-keyword: capacity:;
  each-subclass slot name-prefix :: <string> = "Storage", setter: #f;
  slot identifier :: <string>, required-init-keyword: id:;
  slot connected-to :: <simple-object-vector>;
end class <vehicle-storage>; 

// By using the name-prefix each-subclass slot, we share one say method
// for all vehicle containers
define method say (storage :: <vehicle-storage>) => ()
  format-out("%s %s", storage.name-prefix, storage.identifier);
end method say; 

define method object-fits?
    (object :: <physical-object>, container :: <vehicle-storage>)
 => (fits? :: <boolean>)
  let object-size = object.physical-size;
  let container-capacity = container.storage-capacity;
  object-size.length < container-capacity.length
    & object-size.height < container-capacity.height
    & object-size.width < container-capacity.width;
end method object-fits?; 

// Vehicle storage that can hold only one aircraft regardless of direction
// Direction in this context is either #"inbound" or #"outbound"
define abstract class <single-storage> (<vehicle-storage>)
  slot vehicle-currently-occupying :: false-or(<aircraft>) = #f;
end class <single-storage>; 

// Vehicle storage that can hold multiple aircraft, with distinct queues
// for each direction
define abstract class <multiple-storage> (<vehicle-storage>)
  slot vehicles-by-direction :: <object-table> = make(<object-table>);
  slot maxima-by-direction :: <object-table> = make(<object-table>);
  keyword directions:;
  keyword maxima:;
end class <multiple-storage>; 

// In a real airport, there would be many paths an aircraft could take
// For our simple airport example, we define only the #"inbound" and 
// #"outbound" paths
// The directions parameter is a sequence of these aircraft path names
// Multiple storage containers can limit the number of aircraft that 
// they can hold for each path; this is the maxima parameter
// This initialize method creates a queue to hold aircraft for each
// direction, and stores the queue in a table indexed by direction
// This method also stores the maximum number of aircaft for that 
// direction in a different table
define method initialize 
    (object :: <multiple-storage>, #key directions :: <sequence>, 
     maxima :: <sequence>)
  next-method ();
  for (direction in directions,
       maximum in maxima)
    object.vehicles-by-direction[direction] := make(<deque>);
    object.maxima-by-direction[direction] := maximum;
  end for;
end method initialize; 

// From the preceding basic vehicle containers, we can build specific
// containers for each aircraft-transition location
define class <gate> (<single-storage>)
  inherited slot name-prefix, init-value: "Gate";
end class <gate>; 

// Given a zero-based terminal number, and a one-based gate number, create
// an return a string with a gate letter and a terminal number in it
define method generate-gate-id 
    (term :: <nonnegative-integer>, gate :: <positive-integer>)
 => (gate-id :: <string>)
  format-to-string("%c%d", $letters[term], gate);
end method generate-gate-id; 

// Gates-per-terminal is a vector; each element of the vector is the
// number of gates to create for the terminal at that index
// Returns a vector of all the gate instances
define method generate-gates
    (gates-per-terminal :: <vector>, default-gate-capacity :: <size>)
 => (gates :: <vector>)
  let result = make(<vector>, size: reduce1(\+, gates-per-terminal));
  let result-index = 0;
  for (term from 0 below gates-per-terminal.size)
    for (gate from 1 to gates-per-terminal[term])
      result[result-index]
        := make(<gate>, id: generate-gate-id(term, gate),
                capacity: default-gate-capacity);
      result-index := result-index + 1;
    end for;
  end for;
  result;
end method generate-gates; 

// This class represents the part of the airspace over a given airport
define class <sky> (<multiple-storage>)
  // The airport over which this piece of sky is located
  slot airport-below :: <airport>, required-init-keyword: airport:;
  inherited slot name-prefix, init-value: "Sky";
  required keyword inbound-aircraft:;
end class <sky>; 

// When a sky instance is created, a sequence of inbound aircraft is
// provided 
// This method initializes the direction slot of the aircraft to 
// #"inbound", and places the aircraft in the inbound queue of the sky
// instance
define method initialize 
    (sky :: <sky>, #key inbound-aircraft :: <sequence>)
  next-method(sky, directions: #[#"inbound", #"outbound"],
              maxima: vector(inbound-aircraft.size,
                             inbound-aircraft.size));
  let inbound-queue = sky.vehicles-by-direction [#"inbound"];
  for (vehicle in inbound-aircraft)
    vehicle.direction := #"inbound";
    push-last(inbound-queue, vehicle);
  end for;
  // Connect the airport to the sky
  sky.airport-below.sky-above := sky;
end method initialize; 

// This class represents a strip of land where aircraft land and take off
define class <runway> (<single-storage>)
  inherited slot name-prefix, init-value: "Runway";
end class <runway>; 

// Taxiways connect runways and gates
define class <taxiway> (<multiple-storage>)
  inherited slot name-prefix, init-value: "Taxiway";
end class <taxiway>; 

In the preceding portion of the airport-classes.dylan file, we define the tangible objects that represent the various normal locations for aircraft in and around an airport. These locations are known as containers or vehicle storage. We can connect vehicle-storage instances to one another to form an airport. Instances of <single-storage> can hold only one aircraft at a time, whereas instances of <multiple-storage> can hold more than one aircraft at a time. Also, instances of <multiple-storage> treat inbound aircraft separately from outbound aircraft. We define the object-fits? method, which determines whether a physical object can fit into a container. We also define methods for creating, initializing, and describing various containers. Note the use of the each-subclass slot name-prefix, which permits one say method on the <vehicle-storage> class to cover all the vehicle-container classes. Each subclass of vehicle storage can override the inherited value of this slot, to ensure that the proper name of the vehicle storage is used in the description of instances of that subclass.

The <vehicle-storage>, <multiple-storage>, and <single-storage> classes are all abstract, because it is not sensible to instantiate them. They contain partial implementations that they contribute to their subclasses.

In the generate-gates method, the gates-per-terminal parameter is a vector that contains the count of gates for each terminal. By adding up all the elements of that vector with reduce1, we can compute the total number of gates at the airport, and thus the size of the vector that can hold all the gates.

Next, we examine the classes, initialization methods, and say methods for the vehicles in the application.