Types as Objects Pattern

The concept of type or description occurs in many application domains. Modelers often have to decide if a domain type should be represented in the model as a class or an object.

If an object can have multiple types, or if the type of an object doesn't affect its behavior, or if the number of types would produce an unwieldy inheritance hierarchy, then consider representing types as objects.

The Types-as-Objects Pattern (TOP) can be found remotely or locally.

Here's the general pattern:

A more interesting version of the pattern allows types to be instances:

Implementation

We could implement this pattern directly to produce reusable classes:

class Instance {
   protected InstanceType type;
   public Instance(InstanceType type) {
      this.type = type;
   }
   public InstanceType getType() {
      return type;
   }
}

class InstanceType extends Instance {
   private String name;
   public static final InstanceType TypeType
      = new InstanceType("Type");
   static { TypeType.type = TypeType; }
   public InstanceType(String name, InstanceType type) {
      super(type);
      this.name = name + "Type";
   }
   public InstanceType(String name) {
      this(name, TypeType);
   }
   public String toString() {
      return name;
   }
   public Instance makeInstance() {
      return new Instance(this);
   }
}

Here's how this could be used in, say, a video rental store:

InstanceType sciFi = new InstanceType("ScienceFiction");
InstanceType matrix = new InstanceType("Matrix", sciFi);
Instance dvd = matrix.makeInstance();
InstanceType type = dvd.getType();
for(int i = 0; i < 5; i++) {
   System.out.println(type);
   type = type.getType();
}

Here's the output produced:

MatrixType
ScienceFictionType
TypeType
TypeType
TypeType

Alternatively, we could simply regard Instance and InstanceType as collaboration parameters that are bound to description classes.

Implementation

Genre is the type class for Film:

class Genre {
   private String name;
   public Genre(String name) {
      this.name = name;
   }
   public String toString() {
      return name;
   }
   public Film makeInstance(String title) {
      return new Film(title, this);
   }
}

Note that we could have other types for films, such as G, PG, R, X, etc.

A film is a description of what's on a recording:

class Film {
   private String title;
   private Genre type;
   public Film(String title, Genre type) {
      this.title = title;
      this.type = type;
   }
   public Recording makeInstance() {
      return new Recording(this);
   }
   public Genre getType() {
      return type;
   }
   public String getTitle() {
      return title;
   }
   public String toString() {
      return title;
   }

}

Recordings:

class Recording {
   private Film type;
   private boolean isRented;
   public Recording(Film type) {
      this.type = type;
      isRented = false;
   }
   public Film getType() {
      return type;
   }
   public boolean isRented() {
      return isRented;
   }
   public void setRented(boolean flag ) {
      isRented = flag;
   }
}

Advantages of type objects

Type objects are great if the corresponding types in the domain are constantly changing. Changing the type of an object is simply a matter of setting the object's type link to a new type. In programming this can be done while the program is still running.

Run time type identification means that objects know their types. It's not always the case that an object knows what class it is an instance of, but requiring an object to know its type tag is simple.

Sometimes a class can be divided into types in multiple ways. This can lead to confusing inheritance hierarchies. For example, there are two ways to answer the question "What type of film was it?"

Note that a film like "Shawn of the Dead" might belong to several of these classes.

Here's a simpler solution:

Disadvantages of types as objects

Suppose that in addition to altitude and air speed, military planes had a payload attribute (i.e., how many bombs can it carry). If types are objects, then we can simply add payload to the attribute list for military planes:

But if we use type tags, then here's what a typical military plane looks like:

Where are we supposed to put payload?

Suppose also that the implementation of the takeoff operation depends on the type of airplane. If types are objects, then we will need a multi-way conditional to implement takeoff:

class Airplane {
   AirplaneType type;
   void takeoff() {
      if (type == AirplaneType.MILITARY) { /* algorithm 1 */ }
      else if (type == AirplaneType.COMMERCIAL) { /* algorithm 2 */ }
      else if (type == AirplaneType.PRIVATE) { /* algorithm 3 */ }
      else error("Unknown airplane type");
   }
   // etc.
}

Object-oriented programmers dislike this style of coding because it centralizes intelligence. It's better to distribute intelligence by allowing each subclass of Airplane to redefine the takeoff method.

Related Patterns

The Quantity Pattern

Another pattern replaces numbers such as doubles and integers by quantities. A quantity is a number combined with a unit. For example: 3.14 US Dollars, 3.14 kilograms, 3.14 seconds.

This diagram adds a navigation arrow and a role name to the Unit end of the association. The navigation arrow indicates that given a quantity we can discover the associated unit, but not vice versa. The role name indicates the preferred name a quantity object uses to refer to its unit. Note also that the attribute types have also been indicated.

During implementation we might translate this diagram into the following Java code:

public class Unit {
   public String name;
   // etc.
}

public class Quantity {
   public Unit type;
   public double amount;
   // etc.
}

We can take advantage of encapsulation by declaring attributes and associations to be private:

This means that these attributes and associations will be hidden from client classes. We can further specify that these attributes are read-only or read-write as dictated by logic. During implementation the corresponding constructors (initializers), getters, and setters may be declared:

public class Unit {
   private String name; // read-only
   public Unit(String name) { this.name = name; }
   public String getName() { return name; }
   // public way to make quantities:
   public Quantity makeQuantity(double amt) {
      return new Quantity(amt, this);
   }
   // etc.
}

public class Quantity {
   private Unit type; // read-only
   private double amount; // read-write
   // package scope, no public access:
   Quantity(double amount, Unit type) {
      this.amount = amount;
      this.type = type;
   }
   public Unit getType() { return type; }
   public double getAmount() { return amount; }
   public void setAmount(double amount) {
      this.amount = amount;
   }
   // etc.
}

Property List

Lazy Load