Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Refactoring to Adaptive Object Modeling: Type-Object

0.00/5 (No votes)
20 Jun 2005 1  
Adaptive Object Modeling is a relatively new concept in object oriented design that has found some roots in advanced development processes.

Sample Image - TypeObject.gif

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 Type-Object methodology of Adaptive Object Modeling. Type-Object methodology maintains that the exact type(s), attributes and operations of the shared object data are determined only at runtime through the entity object's entity-type based on the meta-data used to build the instance object.

Background

Using a Type-Object, design method is an interesting way to extend the configuration possibilities of simple class object methodology. It gives a way to define a class and its attributes and operations dynamically, or at runtime, from a database, configuration file or user interface. Virtually any meta-data source can define the entity-type, attributes and operations for a given AOM class structure.

Here we see the exact Adaptive Object Modeling UML for the Type-Object class, based on the PLoPD3 Johnson and Woolf model. Notice that the Entity object accepts and has specific attributes and operations of the EntityType object, while the EntityType object adapts any available attributes and methods, giving complete flexibility to the entity object to add or subtract these pieces as the environment demands.

How to use the code?

We start this refactoring example with the classes ExecutiveJob and EmployeeJob, which both have a simple enough class structure, initialized with a basic constructor. For this example, we need to make these objects more flexible and sensitive to our ever-changing business needs. To accomplish this we need to define dynamically the attributes and operational methods or business rules and relationships associated with this class, as well (and most importantly for this example) we need to dynamically define it's class type. For this example, we will show only the conversion to the type-object pattern.

public class ExecutiveJob
{
    private string _attribute1;
    private string _attribute2;


    public ExecutiveJob()
    {
                
    }             
    ...attributes accessors and methods
}

            
public class EmployeeJob
{            
    private string _attribute1;
    private string _attribute2;


    public EmployeeJob()
    {
                
    }             
    ...attributes accessors and methods
}

The first step to the AOM model is to define the Type of the object that we wish to add metadata to. This class holds all possible attributes for an entity object, and defines the operational methods as well. For this example, both the attributes and operational method instances are omitted, since they are outside the scope of the example. The EntityType object in this example acts as a dynamic template for the class type. We have two EntityType objects, ExecutiveJobEntityType and EmployeeJobEntityType, which serve to define the actual limits of the class Entity definition. (In later articles we will see the significance of this class and how it helps to define how one class can dynamically be set to interact within the system.)

public class EntityType
{                    
    private string _typeName;
    private Type _type;
    
    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;}                    
    }            
                    
}

We now can create our JobEntity class, which accepts EntityType, and is a meta class containing its specific attribute and operations in its EntityType realtionship at runtime. Notice that the class now has no attributes or methods. We will allow the meta-data at runtime to define these for us through the EntityType (which is outside the scope of this article). We associate our entity type class with our entity class in this fashion, giving our entity flexibility to define its class type from run time meta-data. The EntityType class will hold all the attributes and methods for the class, allowing different functional types to be described in an ad-hoc fashion. Below this class is the implementation meta-class ExecutiveJobEntityType and EmployeeJobEntityType, which will contain only the necessary attributes and operations for the class type we want, and helps to define our class.

Note: Remember, reflection is used heavily as we will see in the factory class, and is the key to dynamic representation of the different type variations of the class.

public class JobEntity
{
     private EntityType _entityType;
            
     public JobEntity(EntityType entityType)
     {
         _entityType = entityType;
         Console.WriteLine("..." + this.GetType().Name + 
                    " initialized with EntityType " +
                    _entityType.Name);
                
                
     }
            
     public EntityType EntityTypeOf
     {
         get{return _entityType;}
     }            
 }
        
 public class ExecutiveJobEntityType : EntityType
 {            
     public ExecutiveJobEntityType(string name, Type type) : base(name,type)
     {
         Console.WriteLine("..." + this.GetType().Name + " initialized.");
     }
 }
        
 public class EmployeeJobEntityType : EntityType
 {
     public EmployeeJobEntityType(string name, Type type) : base(name,type)
     {
         Console.WriteLine("..." + this.GetType().Name + " initialized.");
     }
 }

We now are 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 is the meta-data for the entitytype. This is important because we will use this data to build and define the class and which class type to associate with it. We retrieve from our meta-data in the config file the name which we will refer to the class, and the full type name. (In a later article, we will also retrieve the attributes and operations for the entity type.)

        string name = Convert.ToString(hash["name"]);
        //the name of this attribute

        if(name == null || name == string.Empty)
            throw new FactoryException("No entity name specified.");
        //get the entity type

        string strEntityType = Convert.ToString(hash["type"]);
        if(strEntityType == null || strEntityType == string.Empty)
            throw new FactoryException("No Entity Type specified for [type]");
        //get the type for a strongly typed parameter

        Type entityTypeOf = Type.GetType(strEntityType);
        if(entityTypeOf == null)
            throw new FactoryException("Could not load class Type for type " + 
                                      strEntityType + " for  entity " + name);

The meta-data from the config file appears thus:

 <entityTypes>
     <entityType name="ExecutiveJobEntityType" 
                type="TypeObject.ImplClasses.ExecutiveJobEntityType" />
     <entityType name="EmployeeJobEntityType" 
                type="TypeObject.ImplClasses.EmployeeJobEntityType" />
 </entityTypes>

Next we check to see if the Hashtable that will hold our EntityType classes exists and contains the current data. If not we will use .NET's class activation methods to instantiate a new class of the correct type and pass in the appropriate parameters, in this case the name and type.

    if(_entityTypes == null)
        _entityTypes = new Hashtable();
    
    EntityType entityType = 
           (EntityType)Activator.CreateInstance( entityTypeOf,
            new object[]{name,entityTypeOf});
    if(!_entityTypes.Contains(name))
        _entityTypes.Add(name,entityType);

Next we are going to load the actual Entity class. We have three items of data per class, name, class type and entity type.

        //the name of this attribute

        if(name == null || name == string.Empty)
            throw new FactoryException("No entity name specified.");
        //get the entity type

        string strEntityClassType = Convert.ToString(hash["type"]);
        if(strEntityClassType == null || strEntityClassType == string.Empty)
            throw new FactoryException("No Entity Class specified for " + 
                     strEntityClassType);
        //get the type for a strongly typed parameter

        Type entityClassType = Type.GetType(strEntityClassType);
        if(entityClassType == null)
            throw new FactoryException("Could not load class Type for type " +
                     strEntityClassType + " for  entity " + name);
        //get the entity type

        string strEntityType = Convert.ToString(hash["entityType"]);
        if(strEntityType == null || strEntityType == string.Empty)
            throw new FactoryException("No Entity Type specified for " + 
                     strEntityType);

The meta-data from the config file appears thus:

    <entities>
        <entity name="ExecutiveJob" 
            type="TypeObject.ImplClasses.JobEntity" 
            entityType="ExecutiveJobEntityType" />
        <entity name="EmployeeJob" 
            type="TypeObject.ImplClasses.JobEntity" 
            entityType="EmployeeJobEntityType" />
    </entities>

Next, we check to see if the Hashtable that will hold our Entity classes exists and contains the current data. If not we will use .NET's class activation methods to instantiate a new class of the correct type and pass in the appropriate parameters, in this case the entity type.

    if(_entities == null)
       _entities = new Hashtable();
        
    JobEntity entityClass = 
      (JobEntity)Activator.CreateInstance( 
      entityClassType,new object[]{_entityTypes[strEntityType]});
        
    if(!_entities.Contains(name))
      _entities.Add(name,entityClass);

Notice that, we only need the name and actual full type name of the class object and the entity type as a parameter to build this class. This is because the attributes and operational methods will be dynamically loaded to this class type at runtime and are contained in other parts of the config file (out of scope for this article). The attributes and parameters are associated with the EntityType, not the class, so they are decoupled from the class at compile time.

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 the proper EntityType object has been loaded and yielded expected results.

What have we accomplished? Well by looking at this article alone, it seems little. But remember, this is just the first step. We started with two fairly static classes ExecutiveJob and EmployeeJob, which we have now migrated to the AOM pattern. From here we will begin to add Properties, Associations, Strategies (or operational methods), and RuleObjects (business function objects which define where and how we deal with the code flow). All these further innovations will help to define our adaptive object model further and make it into a working schema.

Expanding the Example

How can we expand this example to functional code? We might expand the different ways we input and build object with meta-data, either by using a dynamically configurable data source like a database, or by allowing meta-data to stream in from a client or user interface. The first step is to define the constraints of the application, then to see how many different items in code you can provide meta-data for. Your application meta-data can be from settings changed by a power user, or by any client that you want to be allowed to configure a piece of the application.

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 the 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 might stress that making the effort to use advanced design methodologies will strengthen your overall design ability, but like your basic coding skills, it is something learned and cultivated.

Related Articles

Other articles in this series include:

  1. Properties.
  2. Strategies.
  3. Entity-Relationships and Accountability/Cardinality.
  4. Interpreter methodologies (coming soon).
  5. Ghost Loading, Data Mapping and more...
  6. Discussions on Practical Project usefulness of design.
  7. Final Discussions and MVC.

History

This is the second revision and is the first installment in a series.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here