In a project that I've been involved with for many years, I've gradually evolved a design pattern that's proven to be extremely useful for me. I sometimes feel I should get a bit evangelical with it, but I'd be a bit embarrassed if I tried and found out that it was just my version of somebody's old hat. I've dug through Design Patterns looking for it in vain, and I haven't run across anyone else talking about it, but my search hasn't been exhaustive.
The core idea is having a broker object that manages a set of definition objects, each definition object constituting a possible value of some complex property. As an example, you might have Car, Plane, and Generator classes that all have an EngineType. Car doesn't store its own EngineType object, it stores a reference key of some kind that states the kind of Engine it has (such as an integer or string ID). When we want to look at properties or behavior of an EngineType, say WankelEngine, we ask the EngineTypeBroker singleton object for WankelEngine's definition object, passing it the reference key. This object encapsulates whatever's interesting to know about EngineTypes, possibly simply being a property list but potentially having behavior loaded onto it as well.
So what it's facilitating is a kind of shared, loosely-coupled aggregation, where many Cars may have a WankelEngine but there is only one WankelEngine definition object (and the EngineTypeBroker can replace that object, leveraging the loose coupling into enhanced runtime morphism).
Some elements of this pattern as I use it (continuing to use EngineType as an example):
- There are always IsEngineType(x) and EngineType(x) functions, for determining whether a given value is a valid reference key for an EngineType and for retrieving the EngineType definition object corresponding to a reference key, respectively.
- I always allow multiple forms of reference key for a given EngineType, always at least a string name and the definition object itself, more often than not an integer ID, and sometimes object types that aggregate an EngineType. This helps with debugging, makes the code more flexible, and, in my particular situation, eases a lot of backward compatibility issues relative to older practices. (The usual way people used to do all this, in this project's context, was to define hashes for each property an EngineType might have and look up the properties by reference key.)
- Usually, each definition instance is a subclass of a general class for that definition type (i.e. WankelEngine inherits EngineType). Class files for definition objects are kept in a directory like /Def/EngineType (i.e. WankelEngine's class would be /Def/EngineType/WankelEngine). So related definitions are grouped together, and the class files resemble configuration files for the EngineType, but with the ability to define code (not typically found in configuration files).
Some trivially illustrative sample pseudocode:
class Car {
attribute Name;
attribute EngineTypeCode;
object GetEngineTypeDef() {
return EngineTypeBroker->EngineType(this->GetEngineTypeCode());
}
string GetDescription() {
object def = this->GetEngineTypeDef();
return "I am a car called " . this->GetName() . ", whose " .
def->GetEngineTypeName() . " engine can run at " .
def->GetEngineTypeMaxRPM() . " RPM!";
}
}
So, is there a name out there for this?