Refining the Type Object Pattern:
The Class Object Pattern
Fernando D. Lyardet
LIFIA - Dto. de Informática,
Facultad de Ciencias Exactas, UNLP
fer@sol.info.unlp.edu.ar
CLASS OBJECT (Object Structural)
Intent
As its inspiring antecesor, the Type Object pattern, allows to decouple instances from their class so that those classes can be implemented as instances of a class and provide the ability of adding arbitrary number of subclasses and class attributes.
The refinenment presented here, takes this idea and also allows a dynamically created class to support single inheritance, allowing setting not only it´s superclasses but an arbitrary number of subclasses.
Motivation
As an introduction, we could consider the example presented for the Type Object, the video rental store’s inventory, where since many of the videotapes are very similar, the videotape’s instances would share a lot of redundant information. Following the example, all copies of "Star Wars" are pretty similar: they have the same title, rental price, etc. Thus repeating all that information through all copies of "Star Wars" is redundant.
There are many ways to overcome this problem, for example, subclassing VideoTape for each movie, where each time a movie is added to the system it would be necessary to add a new subclass and recompile the system. This approach is neither practical nor flexible. Similar constraints arise with other strategies such as adding a special class -the UnknownTape- to handle the tapes for a new movie.
Both approaches to a solution that deals with the fact that each type of videotape needs to be an instance of a class of type videotape. Unfortunately, class based languages do not give support for instances of instances of classes.
As
expressed in [woolf 96] the key is to implement two classes, one whose
instances represents application instances and other whose instances
represent what would normally be the application classes. Each
application instance has a pointer to its corresponding class-instance.
So to implement this solution two classes should be implemented: one to
represent the VideoTape and other to represent the Movie.
The last solution adds the flexibility necessary to allow for example, changing the class of a VideoTape in runtime by simply changing its class reference. However, there are a number of posibilities it would be interesting to analize, for instance: to protect from video piracy, the industry adds a special code to their movies, so each new movie would have a special code/identification.
We could solve this problem with the technics described above but, even using the type object pattern, it would be necessary to recompile due to the lack of posibility of changing/adding a class variable to support Movie code.
As another example, we can discuss the design and implementation of CASE environment to support an OO hypermedia design methodology (OOHDM [Schwabe96]). The tool must be able to model every design component as a class, and support any kind of operations upon it, as if they were actual classes. The kind of operations on a class involves adding class and instance attributes, inheritance (is-a relationships) and composition (part-of relationships).
Another goal of the system is to provide the ability of generating a working hypermedia application for a given plattform (html, Toolbook, etc). Such automated implementation should be generated from the OO model given by the user. Thus, the need of supporting instances of model classes is implicit.
At this point, even though the Type Object pattern does not provide the needed functionality, the strategy of having separate classes whose instances behave as classes and instances will be of use to our solution.
The Class Object pattern describes how dinamically created classes can
be extended through inheritance or by adding class and instance
attributes, and the way the instances of these classes are generated.
It also describes how the latter behave as actual instances whose type
and number of attributes may vary in runtime.
To provide a dinamic mechanism for adding attributes to a class, we need to describe them in a way such that the creation of class instances with the specified attributes could be possible. These descriptions of the attributes could be further specialized to specify whether the attributes would be class or instance attributes at the moment of instantiation.
Class Instance
The classes, whose instances represent actual system instances, are all descendants of the InstanceClass, such as AttributeInstance and ClassInstance. The former is the AttributeType counterpart and holds the actual value of an instance attribute value, and the latter represent the instances of a class.
The AttributeType class provides a way to describe a class attribute and plays somehow as a factory method depending on the typeDescriptor value. When an instance of a dinamic class is being created, the class asks to its attributes for an instance of themselves that will belong to the new instance.
The idea of supporting single inheritance through simple dynamic
subclassing mechanism relies on the ability of creating a virtual
concatenation of a class attributes to its superclass attributes.
To provide this feature, each ClassOf instance has:
When the superclass of one of the dinamic classes is set, the new subclass sets its "super" reference to the given class. At this step, further steps could eventually take place in order to allow a class notify to its instances and subclasses.
All dinamically created classes and instances of these classes have access to their attributes through a method to specify whether the attributes are to be fetched by name or through an index.
{Example}
desiredAttribute:=MyObject.attributesByName('MovieRenter');
Or
desiredAttribute:=MyObject.attributesByIndex(indexNumber);
This approach allows hidding whether the attributes are inherited or not by providing a uniform access mechanism. Both ClassOf class and ClassInstance implement the AttributeBy… methods to access their attributes, with some differences:
{Example}
{accessing class atttribute (Videotape´s movie name}
desiredAttribute:=videoTape.Class.attributesByName('MovieName');
Or
{accessing an instance attribute}
desiredAttribute:=videoTape.attributesByName(´MovieRenter´);
Aplicability
Use the ClassOf pattern when:
Structure:
Participants
Consequences
The advantages of the Class Object pattern are:
The disadvantages of the Class Object Pattern are:
Implementation
There are a number of issues to consider when implementing the Class Object pattern:
A word about Delphi implementation: Borland’s Delphi incorporates a new type called Variant whose type does not need to be specified at compile time. This feature allows a cleaner and simpler implementation (much like a Smalltalk implementation), since the problem of typing is eliminated.
Another feature that could be used to enhance the implementation could be the use of named attributes called Properties to hide the actual methods for accessing class or instance attributes.
{Example using properties}
desiredAttribute:=MyObject.Attributes[´Name´];
OR
DesiredAttribute:=MyObject.Attributes[index];
Now we present an outline of the implementation. A complete implementation of this pattern is freely available on the web in both Borland´s Delphi, and Smalltalk (C++ version coming soon!) versions at: http:\\www-lifia.info.unlp.edu.ar\~fer\plop97.html .
Sample Code
Type
TypeObject= Class(TObject)
public
instances:Tlist;
Function CreateInstance:InstanceClass; Virtual;
end;
ClassOF= Class(TypeObject)
Private
Attributes,ClassAttributes:TStringlist; {TstringList works much like a}
{Dictionary in Smalltalk }
Public
Super:ClassOf;
Function attributesCount:integer;
Function attributesByName(aName:String):AttributeInstance;
Function AttributesByIndex(anIndex:Integer):AttributeInstance;
Function InstanceattrByName(aName:String):AttributeInstance;
Function InstanceAttrByIndex(anIndex:Integer):AttributeInstance;
Function CreateInstance:InstanceClass; Override;
end;
Function ClassOf.CreateInstance:InstanceClass;
var newInstance:ClassInstance;
i:Integer;
temp:AttributeInstance;
begin
newInstance:=ClassInstance.create;
For i:=0 to self.Attributes.Count-1 do
begin
temp:=self.InstanceAttrByIndex(i).CreateInstance;
newInstance.Attributes.AddObject(temp.Name,temp);
end;
Result:=newInstance;
end;
Each dinamic class is able to determine whether a required attribute belongs to it or its superclass through the following methods:
{searches the desired attribute by name}
Function ClassOf.InstanceAttrByName(aName:String):AttributeInstance;
var index:Integer;
begin
index:=self.Attributes.IndexOf(aName);
if aux<>-1 then
Result:=self.Attributes.Objects[index]
else
if super<>NIL then
Result:=self.super.attributeByName(aName);
else
Result:=nil;
end;
{Searches the desired attribute by an index}
Function ClassOf.InstanceAttrByIndex(index:integer):ClassObserver;
begin
if ((index<0) or (index>self.attributeCount)) then
Raise EAttributeIndexError.Create('Invalid attribute index number!')
else
Begin
if index<self.PropStartIndex then
Result:= Super.InstanceAttributes[index][Inherited attr =>ask the superclass}
else
Result:= self.Attributes.items[index-PropStartIndex];{indexes an own
Attribute}
end;
end;
where attributesCount is defined as:
Function ClassOfType.attributesCount:Integer;
Begin
If assigned(super) then
Result:=super.attributes.Count+self.FattributeList.count
Else
Result:=self.FattributeList.count;
End;
InstanceClass= Class(TObject)
Class:TypeObject;
end;
ClassInstance = Class(InstanceClass)
Private
attributes:TStringlist; {Works much like a Dictionary in Smalltalk}
Public
Super:ClassOf;
Function attributesCount:integer;
Function attributesByName(aName:String):AttributeInstance;
Function AttributesByIndex(anIndex:Integer):AttributeInstance;
end;
{for complete implementation, see the url specified above or contact the author}
Related Patterns
A ClassOf could notify its instances much like the aSubject does to its Observers in the Observer pattern (page 237, [GHJV95]). This can be implementing by inheriting the notification mechanism from an Observer (not very nice), or it may delegate the notification of its changes to an observer or a more specilized subclas of it that would work the same way aDependencyTransformer does.
All other patterns related to the TypeObject pattern, are also related to the classOf pattern.
References
[GHJV95] |
Gamma, Helm, Johnson, Vlissides. Design Paterns: Elements of Reusable Object Oriented Software. Addison-Wesley, Reading, MA, 1995; |
[Woolf96] |
Woolf, Bobby. The Type Object Pattern. 1996. |
[Schwabe96] |
D. And G. Rossi: "Systematic Hypermedia Design with OOHDM" Procedings ACM Hypertext ‘96. |