Introduction
Adaptive Object Modeling is a relatively unrealized concept in object oriented design that has found some roots in advanced development processes. It allows that applications are in continual flux (or their requirements are) from adjustments to business rules, environments and user requirements. Its precepts are that using metadata for business objects, rules and process flows, instead of hard coded, static method code, makes the application more flexible to user/client needs. The AOM design model also requires that certain domain 'facts' are maintained, i.e. that there are controlled interpretations of the effects of the 'immediate' changes from user-generated metadata.
Adaptive solutions offer us a way to make applications more configurable (regarding workflow) to the end users. It is not a 99% solution and, to my knowledge there is no end-to-end solution that meets all needs, simply because those needs are more directly defined by the business requirements. However, since business requirements change frequently, writing immutable code flow logic leaves very short lives for code. Even the online industry is feeling the need to configure dynamically: if you are a subscriber to most web portals like Yahoo! or MSN you have a user configurable home page where you can decide which items of interest you would like to display on your page.
Most portal technologies are indirectly pointing us to a more adaptive model, simply because they are configurable by end-users or power-users. I see adaptive technologies like Adaptive Object Modeling in the same light as a web portal: some functionality is needed to be coded by developers, like database and business logic, but where and when this logic is implemented should be (following business trends) decided upon by business experts, not developers. Some adaptive models incorporate the non-meta-data into tier models tightly coupled with tier meta-data, which is one way to do it, but I prefer to let the EUD (end user developer) have the hand in how business data is handled, and simply allow the AOM framework to give the actual implementation of when and where back to business experts (which most developers don't have the bandwidth to be).
To better understand and practice the techniques of AOM design, I am writing this and several other articles that explain the usage and conversion (or refactoring effort) from a static code model to AOM design.
This article will deal with Property methodology of Adaptive Object Modeling. Property pattern methodology maintains that the attributes of a class should be held as a collection of properties, as opposed to single instance variables, which can changed at runtime.
Background
Using a Property design method is an interesting way to extend the configuration possibilities of simple class/object methodology. It gives a way to define a class' attributes dynamically, or at runtime, from a database, configuration file or user interface. Virtually any meta-data source can define the attributes for a given AOM class structure.
Here we see the exact Adaptive Object Modeling UML for the Property
class. Notice that the Entity
object accepts and has specific attributes associated with it from Property
. The value of the attribute could be a primitive type, like a string or boolean, or another entity. If the property
attribute contains an Entity
object this is said to become an association, and the encapsulated entity should know its cardinality. We will for this exercise only deal with primitive types, not associations, which will be part of another article.
How to use the code
We start this refactoring example where we left off from our other example article TypeObject. We still have the same Entity
and EntityType
s, and now we would like to dynamically add some attributes or properties. To accomplish this we need to create a class to hold our dynamic attributes that are loaded during runtime. This takes the form of a simple container (or entity-attribute type) object that holds the name, object value and type of the attribute. This is loaded with metadata at runtime for the specific object attribute that we wish to implement. Notice we have also added also a collection object EntityAttributeCollection
, which contains the shared attributes between the Entity
and the EntityType
.
Note: The value
accessor is added in this example, although not meta-data, it serves to illustrate for our console test the way the attributes are dynamically created from runtime data:
public class EntityAttribute
{
private string _attributeName;
private object _attributeObject;
private Type _attributeType;
public EntityAttribute(string name, object obj, Type type)
{
_attributeName = name;
_attributeObject = obj;
_attributeType = type;
}
public string Name
{
get{return _attributeName;}
set{_attributeName = value;}
}
public object Value
{
get{return _attributeObject;}
set{_attributeObject = value;}
}
public Type TypeOf
{
get{return _attributeType;}
set{_attributeType = value;}
}
}
public class EntityAttributeCollection
{
public void Add(EntityAttribute obj)
{
}
}
Here we see that the EntityType
object from our last example has an added parameter of type EntityAttributeCollection
. This attribute collection will allow the Entity
object to have dynamically loaded meta-data driven parameters associated with it at runtime:
public abstract class EntityType
{
private string _typeName;
private Type _type;
private EntityAttributeCollection _attributes =
new EntityAttributeCollection();
public EntityType(string name, Type type)
{
_typeName = name;
_type = type;
}
public string Name
{
get{return _typeName;}
set{_typeName = value;}
}
public Type TypeOf
{
get{return _type;}
set{_type = value;}
}
public EntityAttributeCollection Attributes
{
get{return _attributes;}
set{_attributes = value;}
}
}
We are now ready to focus on the AOMFactory
class, which is a static factory implementation loosely based on Deyan Petrov's DataSourceFactory
. The factory is where we actually will load our runtime data for this example and is considered the Interpreter model of the AOM pattern. Our example code will use an XML config file for the actual meta-data, but in actual project code, as I stated above a variety of sources are available, not in the least, the client or user interface. Remember, this factory class is only for this example, and while I would recommend a factory or builder pattern for this piece, it is up to you the developer to find the suitable method for loading your classes from meta-data.
The first piece of data we get after this revision to our TypeObject example is the meta-data for the Entity
attribute. We will use this data to build and define all the possible types of attributes we can load to our entity types, making them available as needed to specific entities. We are pulling in name, full type name (and value, which is not meta-data).
string name = Convert.ToString(hash["name"]);
The meta-data from the config file appears thus:
<entityAttributes>
<entityAttribute name="Attribute1"
attributeType="System.String"
attributeValue="This is a Adaptive Object Model Attribute" />
<entityAttribute name="Attribute2"
attributeType="System.Int32"
attributeValue="1" />
<entityAttribute name="Attribute3"
attributeType="System.Boolean"
attributeValue="True" />
<entityAttribute name="Attribute4"
attributeType="System.DateTime"
attributeValue="03/01/2005" />
</entityAttributes>
Next we check to see if the Hashtable
that will hold our EntityAttribute
classes exists and contains the current data. If not we will build the new attribute passing in the appropriate parameters.
if(_entityAttributes == null)
_entityAttributes = new Hashtable();
EntityAttribute attribute = new EntityAttribute(name,
attributeValue,attributeType);
if(!_entityAttributes.Contains(name))
_entityAttributes.Add(name,attribute);
Next we are going to load the actual EntityType
class with the attributes specified in the meta-data from the config file. We parse a comma delimited string to get the various attribute names for each EntityType
, and load each type:
string[] attributes =
Convert.ToString(hash["attributes"]).Split(new char[]{','});
.......
EntityType entityType = (EntityType)Activator.CreateInstance(entityTypeOf,
new object[]{name,entityTypeOf});
foreach(string attribute in attributes)
if(!entityType.Attributes.Contains((EntityAttribute)_entityAttributes[attribute]))
entityType.Attributes.Add((EntityAttribute)_entityAttributes[attribute]);
The meta-data from the config file appears as below. Notice the attributes
XMl node. This is where we put all the possible attribute types by name for each EntityType
instance. The attribute names are comma delimited:
<entityTypes>
<entityType name="ExecutiveJobEntityType"
type="Properties.ImplClasses.ExecutiveJobEntityType"
attributes="Attribute1,Attribute3" />
<entityType name="EmployeeJobEntityType"
type="Properties.ImplClasses.EmployeeJobEntityType"
attributes="Attribute2,Attribute4" />
</entityTypes>
We now are at a point where we can test our code:
Here we see that the entity is first established from the entity type and its attributes have been loaded.
Expanding the example
How can we expand this example to functional code? Next we need to add operational methods in a collection object as an instance on EntityType
. Next we could apply relationship and business rules objects, as well as strategy classes. These items we will cover in the next article. What can we do with this example? Let's say you wanted to allow power users to change the attributes for different class types in your AOM model. A better example would be allowing a user to dynamically configure his default page for a website. Each object attribute that is displayed could be held in meta-data and changed as the client saw fit. We will cover more practical project uses in a later article.
Points of interest
This is the first installment in the series I am writing on real world adaptive object modeling. All examples and the bulk of this article are taken from my professional experience as an architect. The examples given are templates only, and the designer must keep in mind that they are the ones who must decide where different patterns, if any, may be best used in their code.
Deciding to perform a refactoring effort from existing code to a pattern or enhanced design model must be weighed on the necessity and need of the code itself. Patterns and Adaptive Models are only design templates, helpers to accommodate better overall design. I must stress that making an effort to use advanced design methodologies will strengthen your overall design ability, but like your basic coding skills, it is something that is to be learnt and cultivated.
Related articles
Other articles in this series include:
- TypeObject
- Properties
- Strategies
- Entity-Relationships and Accountability/Cardinality.
- Ghost Loading, Data Mapping and more...
- Interpreter methodologies (coming soon).
- Discussions on Practical Project usefulness of design.
- Adaptive Object Modeling: Final Discussions and MVC.
History
This is the first revision and is the second installment in a series.