Adaptive Dynamic Subclassing Design Pattern

Name

Adaptive Dynamic Subclassing

Author

Robert Familiar
bofam@kafka.iii.net

Intent

The Adaptive Dynamic Subclassing Design Pattern outlines an approach to class migration. It defines a mechanism by which an object can have its internal representation dynamically changed from one subclass to another at run time.

Motivation

Applications that want to dynamically alter the internal representation of an object should use this pattern. For example an application may want to migrate a Person object from being a Student to being an Employee without losing the unique identifier which is the Person object.

Consider the following class dictionary that defines 2 dynamic subclasses of Person, Student and Employee:

	Person = <state> PersonState .
	PersonState : Student | Employee .
	Student = .
	Employee = .
At run time, the Person object can be migrated to be an Employee using the rset_[member] method that is generated as part of the class by Demeter:
	// Create a Person object that has a Student internal
	// representation

	Person * pPerson = new Person( new Student() );
	...
	// Now dynamically change the subclass of Person from
	// Student to Employee using rset

	delete pPerson->rset_state( new Employee() );
The method rset_state() takes the new representation for the Person object, storing it in state and returns the previous object representation. Note the deletion of the return value form rset_state(). This is to insure that the Student object is deleted.

Adaptive programs are robust with respect to the class structure changes implied by the Adaptive Dynamic Subclassing Pattern. (See the Demeter book for more information about Demeter.) For example, *from* Company *to* Student will count the number of students employed by a company whether Student is a subclass of Person or not. Also, *from* Company *via* *to* Student with a wrapper at Person referencing Person information will also work in both cases.

An adaptive program defined as a set of propagation patterns will devote a subset of its patterns to handling subclass migration for those dynamic subclasses of interest.

Requirements

The goal is to keep the object id the same but change the object's internal representation.

Refering back to the Person example, at run time you want to keep the object id, i.e. the pointer to the person object, but change the Person object from a Student internal representation to an Employee internal representation.

Also, note that the Student object has the same identifier as the Person object. If you were to count the number of Persons in a group of 5 Students, the answer would be five. If you were to migrate all five Students to be Employees, the number of Persons would still be five and they would be the same Persons as the five Students, but now acting as Employees.

Implementation

The bacic technique to dynamic subclassing is to convert static subclasses to be subclasses of the dataype of a member of the class which was the original base class. For example, consider the following class dictionary
	Vehicle : Truck | Car .
	Truck = .
	Car = .
To convert this class dictionary to use dynamic subclasses, you define a new abstract class for the static subclasses of Vehicle, call it VehicleType. Then make Vehicle a construction class with a data member called vehicleType that is of type VehicleType:
	Vehicle = <vehicleType> VehicleType  .
	VehicleType : Car | Truck .
	Car = .
	Truck = .
It is possible to create other dynamic subclasses of Vehicle that represent other partitions of functionality. For example, we may want to represent different engine kinds that can be coupled with the different vehicle types:
	Vehicle = <engineKind> EngineKind 
		  <vehicleType> VehicleType  .
	EngineKind : Gas | Deisel | Electric .
	Gas = .
	Deisel = .
	Electric .
	VehicleType : Car | Truck .
	Car = .
	Truck = .
It is now possible with this new class dictionary to create a Vehicle object that represents a Gas Car or a Deisel Truck. It is also possible to migrate between these combinations.

For example, at run time a Vehicle object with an internal representation of Gas Car can be migrated to be an Elecletric Car or to being a Deisel Truck without losing the Vehicle object id.

This class dictionary can also be modified to add additional classes for defining Cars and Trucks. The subclasses of Car and Truck replace Car and Truck as dynamic subclasses of Vehicle. Car and Truck are now on the path between Vehicle and a subset of its dynamic subclasses, i.e. those derived from Car and Truck:

	Vehicle = <engineKind> EngineKind
	  	  <vehicleType> VehicleType .
	EngineKind : Gas |
		     Deisel |
		     Electric *common* <amtFuel> DemNumber . 
	VehicleType : Car |
		      Truck *common* <numDoors> DemNumber .
	Gas = "gas" .
	Deisel = "deisel" .
	Electric = "electric" .
	Car : SportsCar | FourDoorSedan | StationWagon .
	Truck : PickupTruck | FourWDriveTruck | GarbageTruck .
	SportsCar = "sports-car" .
	FourDoorSedan = "4-door-sedan" .
	StationWagon = "station-wagon" .
	PickupTruck = "pickup-truck" .
	FourWDriveTruck = "4-wheel-drive-truck" .
	GarbageTruck = "garbage-truck" .
In addition to the propagation patterns that define core functionality, an application will want to define a set of propagation patterns to handle all the cases of object migration that apply to the application. These are called Migration Propagation Patterns

To illustrate, here are two migration propagation patterns used by an application that is modeling the fuel efficiency of a sports car that have both an electric and gas engine.

The application wants to be able to dynamically change the sports car's engine from Gas to Electric and back again to determine the optimal combination. The patterns are passed a Number object for setting the fuel amount of the new engine and a Number object for storing the previous engines fuel amount.

*operation* void SwitchToGas( DemNumber * pGasolene, DemNumber * pVoltage )

  *wrapper* Vehicle
    (@
     EngineKind * pOldEngine;
     pOldEngine = rset_engineKind( new Gas() );
     engineKind->set_amtFuel(pGasolene);
     pVoltage = pOldEngine->get_amtFuel();
     delete pOldEngine;
    @)

*operation* void SwitchToElectric( DemNumber * pVoltage, DemNumber * pGasolene )

  *wrapper* Vehicle
    (@
     EngineKind * pOldEngine;
     pOldEngine = rset_engineKind( new Electric() );
     engineKind->set_amtFuel( pVoltage );
     pGasolene = pOldEngine->get_amtFuel();
     delete pOldEngine;
    @)
Another Design Pattern that would be useful when using dynamic subclasses is the Momento Design Pattern that is documented in 'Design Patterns' by the GOF. This pattern describes a mechanism for taking the state of an object.

An adaptive program may wish to use this mechanism to save the state of a subclass before migrating to another so that the state can be restored when the object migrates back to that subclass.

Theoretical Background

The theoretical underpinings of Dynamic Subclasses can be found in the paper 'Using Dynamic Classes and Role Classes to Model Object Migration' by Wieringa, Jonge and Spruit in TAPOS 1(1), pages 61-83.

Related Patterns

The Adaptive Role Playing Design Pattern defines another approach to class migration. If class Student and class Employee are defined as Role Classes of class Person, then the Person object would be able to play the role of one or more Employees and/or one or more Students. Depending on how you group the Role classes, set the cardinality constraints and set the migration rules will define role playing by the Person class.