Table of Contents
“Dynamic Attributes” is a well-known software design pattern and has been implemented in various forms in different programming languages. When you look at the Java Beans specification and the ActiveX technology from Microsoft, you will find the pattern implemented as property accessor methods. In either case the exposed object data are called properties, yet they have essentially the same meaning as to what we call attributes.
You can also discover attributes in graphical applications, such as from
Adobe Photoshop®, where they are used
to describe the 'properties' of an image. Wherever you look, the pattern is
present. We use the word Attribute in our implementation because it is a more
general term that does not carry connotations to other pattern implementations.
Generally speaking, the structure of an Attribute is quite simple. In every
case an instance contains an alphanumeric name (the Attribute key)
and a valid Object which represents the value. Extending the Attribute
interface is permitted as well. Because there can be no fixed definition of
what an Attribute must contain or do, you are free to extend or implement
your own class.
Attributes can be generally divided into two groups: known and unknown. The former group is usually implemented as instance variables of a class while the latter group is most often stored in a table or array. Just like known attributes, unknown attributes also require a name or key, so that you may look them up in a context. They must also have a value, without which it makes little sense to create an instance in the first place.
Whenever a programming context dictates that it is required to know the type
of value an Attribute holds, it must be available. For example, if you
create a dialog box where a user may change the value of an Attribute, you
must know the Attribute type in order to create the appropriate control at
runtime. A spinbutton for an Attribute would be suitable if its type is Integer.
Sometimes an Attribute must be restricted to predefined values. Imagine, if
you will, that an Attribute should represent a soccer team from the main
league. Surely that Attribute value has to be restricted to the soccer
teams that are actually in the main league. Another example could be an
Attribute value representing the square of another value, which is restricted
to positive real numbers.
Attributes can also have relationships among themselves. Imagine you have a
picture that you wish to scale proportionally. Whenever the Attribute
representing the picture width changes, the dependent height Attribute
must be updated. Because the two values are interdependent, there must be a
definable relationship between them.
Finally, an Attribute requires a context or container to live within. This
container should make handling its attributes as easy as possible.
As previously mentioned, attributes can be divided into two groups: known and unknown. All known attributes are usually implemented as class instance variables. While this technique is indeed a very good programming standard, it does not allow you to decouple an instance variable from its containing class object. For this reason we cannot support this implementation standard while supporting a generic solution. Therefore, we chose to implement the dynamic unknown attribute pattern.
An Attribute encapsulates a name-value pair and provides type
information for that value at runtime. Furthermore, this interface is
responsible for accepting and applying a Constraint object
which can restrict its value and value type.
In its simplest form, an Attribute consists of:
A Name of type String.
A Value of type Object.
The Attribute interface offers the following methods to support its
basic responsibilities:
String getName()
Returns the Attribute name.
Object getValue()
Returns the Attribute value.
void setValue(Object)
Sets the Attribute value.
Since an Attribute name is immutable, the interface requires only one
method to access it. The value, however, can potentially change. Therefore
the interface requires one method to access the value and another to change it.
The AttributeType interface represents the Class
an Attribute value belongs to.
The AttributeType interface provides these methods:
Class getTypeConstant()
Returns the Class object identifying the type.
void setTypeConstant(Class)
Sets the type identifier.
The AttributeType of an Attribute can be accessed with this
method:
AttributeType getType()
Returns the current values type.
In the section on Constraints (the section called “
Constraints
”) we will
examine the interface which marks an object to be Restrictable. Here we
show how this interface is derived by the Attribute interface to include
Constraint support.
Interface Restrictable defines these methods:
Constraint getConstraint()
Returns the root of the constraint tree.
void setConstraint(Constraint)
Sets the root of the constraint tree.
Finally, a programmer must be able to clone an Attribute. To mark an
Attribute as having this ability, the Attribute interface extends
interface Cloneable and supports
the following method invocations:
Object clone()
Clones this attribute. The name of the attribute is not deep-copied, as
String instances are immutable. The value of the attribute is
copied by means of the clone method of
Object.
Object cloneValue()
Clones this attributes value. Usually a deep copy is returned. The value object itself can decide what is to be considered a deep-copy. Sometimes in case of (nested) references to immutable objects a deep-copy may be the same as a shallow-copy.
Example 5.1. Cloning An Attribute
AttributeFactory factory= AttributeFactory.newInstance();
Attribute a= factory.newAttribute("vorname", "hans");
Attribute b= factory.newAttribute("nachname", "fuchs");
// create a cloned version of Attribute a
Attribute c= (Attribute) a.clone();
© 2004, 2005 Tensegrity Software GmbH