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:

 

  1. Class instance references ClassObject: since the programing language does not know the relationship between a ClassInstance object and a ClassOf object, the relationship and meaning of the class reference of ClassInstances must be established by the software designer.
  2. Changing dynamically the class of instances: changing dynamically the class of an instance may take a little effort, since the idea of creating a full class does not allow us asume the new class will have a similar structure (as it happens with the type object). We have to ensure instance attributes are consistent in meaning and structure with the class. Mutate the attributes could be a possibility, matching instance attributes with the new class attributes by name, and adding/deleting remaining attributes.
  3. Reflecting Class changes to their instances: reflecting class changes to its instances is a class responsibility. In languages such as Delphi or C++ do not provide their basic Object classes with the ability of sending a "self changed:" message. Thus, the notification capability should be implemented either by the ClassObject itself or delegating this functionality to specialized objects that behave as Observers or DependencyTransformers (both also custom-programmed).
  4. Attributes ability for holding values: AttributeInstance instances have the ability of holding the actual attribute’s data (value). For pure OO languages such as Smalltalk, this is a trivial issue. The problem arises with hybrid OO languages such C++, where type information is needed at compile time. An easy way to overcome this problem could be the following:

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.