Click here to Skip to main content
Click here to Skip to main content

Refactoring to Adaptive Object Modeling: Practical Uses

, 20 Jun 2005
Rate this:
Please Sign up or sign in to vote.
This article will deal with how to make the AOM pattern methodology useful in actual projects.

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 how to make the AOM pattern methodology useful in actual projects. I skipped ahead a bit to write this article (relationship and interpreter articles will come later) because I saw a real lack of understanding revolving around real world applications and usage of this model. This article will deal with application, implementation and conversion techniques for current application code to the AOM Model. Don't get confused. This will not be a regurgitation of the current writings on the methodology, but (I hope) a different approach to describing the methodology intended to help build competencies in the pattern. When I am done, you may think this is not AOM at all, but what I am trying to get across is some relevant ideas that the model suggests. I will pick apart different pieces of the overall model in this article, so that I can better illustrate the different ways you might use the disparate pieces in projects.

Background

Let us first put aside some common misconceptions. First, this is not the holy grail. It is a pattern, and the usefulness of a pattern is in how it can be applied to real world coding. While the pattern may appear overly complex, we will attempt in this article to only use pieces of the puzzle in different ways, in the hope that we might shed some light on the cognizant whole.

Here we see a version of the Type Square Adaptive Object Modeling class pattern UML based on the ECOOP & OOPSLA 2001 Yoder, Balaguer, Johnson model. Notice that the Entity object accepts and has specific operations (or strategies) and attributes (or properties) are associated with it. This is not the exact UML of course, for type square, but it is an accurate representation for this article of what I would like to present. We have class types called entities, entity types that include operational strategies, and attributes as properties. We have left off entity relationships at this time, so we can better illustrate some pieces that I feel hold more value. Not that the Entity-Relationship pattern should be ignored, it is just out of scope, for the time being, of this particular article.

How to use the code?

As we saw above in the UML, we have linked operational methods and attributes to a class, and they are not part of the class itself, but exist as component parts linked through the class EntityType. This is the most useful and interesting piece of the pattern I have found, due to its unique flexibility of design. Imagine, you build a class, then your business user tells you that need to add some more functionality to the class. Well, normally you would shake your head, and go and recompile the class code with the changes. This is costly in build time, as well you have to factor in the impact to your current design. But what if you could add any attributes and/or methods you wished without the recompile? What if you could link entire assemblies together at runtime, simply by changing some configuration data? I will show you how easy it is.

First, let's look at the most poignant aspect I found of the AOM model. We will attempt to re-write classes with immutable compiled methods into Entity and EntityType patterns, and define these same methods in another assembly. We start out with the class EmployeeJob, which has two methods DoSomeWork and GoofOff.

            public class EmployeeJob
            {           

                public EmployeeJob()
                {
                
                }         
                    
                public void DoSomeWork(string typeOfWork)
                {
                    //some functional code here

                }
                
                public void GoofOff()
                {
                    //some functional code here

                }
            }

Let's say, we wanted this class to have four new methods: GoToLunch(string restaurant), TakeABreak(), WriteAReport(string reportName) and ClockOut(). We could add them to the class itself, but we have no way of telling when we will have to add methods again, and we also need to add these methods to a new class we will create: ExecutiveJob. We could create a parent class containing the common methods and subclass EmployeeJob and ExecutiveJob, but if the code base grows very large and we have a large variety of job types, this can become unmanageable. Let's say that, at sometime in the future fifty new job types will be added, each with its own specific needs for functionality. That would mean (at least) fifty new subclasses just like EmployeeJob and ExecutiveJob. Wow! How do we deal with all those classes. AOM can help here. With the AOM model as our base we can create virtual classes at runtime with all the necessary attributes and methods we need for each class, using configuration meta-data. Meta-data, as we know is machine understandable data that describes object states (not values). Let's look at how we start.

Let's first start by providing a way to retrieve operational data from any assembly.

Below we see an interface and a collection that will hold these interfaces. The interfaces will be a link to all the possible methods in any assembly, that exists in a class that inherits from IOperation and implements the method OperationMethod. We give OperationMethod a very generic parameter list object[] parameters, which allows us to pass arguments in any number and type to the method. This gives us the flexibility to define any method strategy desired in the implementation class. We also have a collection objects which will hold these operational strategy methods inside the specific EntityType.

Note: We might replace this later with a delegate pointer in the interface to the method on the implementation class, to better type the actual method call.

        public interface IOperation
        {
            void OperationMethod(object[] parameters);
        }
        
        //Collection of attributes

        public class OperationsCollection
        {
            public void Add(IOperation obj)
            {
                //.....addition code to collection
            
            }
        
        }

Our meta-data is pulled for this example from a config file (XML). Notice that each operation definition has XML attributes: name, assembly and type. The assembly XML attribute defines which assembly (or .dll) you wish to pull the method from. The type XML attribute tells us which actual class to use, that inherits from IOperation and implements the method OperationMethod. We now need to build each class in its own assembly that we want as a strategy, and implement the interface method OperationMethod. The config file excerpt below shows how we want to define the classes, and their container assemblies.

     <entityOperations>
        <entityOperation name="DoSomeWork" 
            assembly="OperationalStrategies" 
            type="OperationalStrategies.DoSomeWork" />
        <entityOperation name="GoofOff"             
            assembly="OperationalStrategies" 
            type="OperationalStrategies.GoofOff" />
        <entityOperation name="GoToLunch"             
            assembly="OperationalStrategies" 
            type="OperationalStrategies.GoToLunch" />
        <entityOperation name="TakeABreak"             
            assembly="OperationalStrategies" 
            type="OperationalStrategies.TakeABreak" />
        <entityOperation name="WriteAReport"             
            assembly="OperationalStrategies" 
            type="OperationalStrategies.WriteAReport" />
        <entityOperation name="ClockOut"             
            assembly="OperationalStrategies" 
            type="OperationalStrategies.ClockOut" />        
    </entityOperations>

Note: The above XML is considered meta-data for this application example.

Each one of these method strategies can be added dynamically to the class type via its EntityType class at runtime.

We now need to create functional method objects inheriting from IOperation, which define exact method operations we will allow to be adapted to our entity object. Having the interface allows us to tie that interface to any class method we wish, as long as that class implements IOperation.

Note: The operational methods could be contained in other assemblies, the namespace encapsulation becomes unimportant, because you can add attributes or methods via the metadata, without concern to its source at compile time. Also the operation method interface IOperation could be changed to dynamically define strongly type parameters as well, but this will be saved for either another article or your own invention. Remember, reflection is used heavily as we will see in the factory class, and is the key to dynamic representation of contractual interfaces.

        public class DoSomeWork : IOperation
        {
            void IOperation.OperationMethod(object[] parameters)
            {
                Console.WriteLine("..." + this.GetType().Name + 
                            ".OperationMethod method called.");
                foreach(object obj in parameters)
                    Console.WriteLine("... parameter Type:" + 
                             obj.GetType() + " Value:" + obj);  
            }
        } 

        public class GoofOff : IOperation
        {
            void IOperation.OperationMethod(object[] parameters)
            {
                Console.WriteLine("..." + this.GetType().Name + 
                            ".OperationMethod method called.");
                foreach(object obj in parameters)
                    Console.WriteLine("... parameter Type:" + 
                              obj.GetType() + " Value:" + obj);  
            }
        }
    
        //.....other methods

Now that we have built out both our meta-data and the interface links to our classes, we can define our EntityTypes, which are like templates for our different class types. The EntityType classes allow us to map groupings of types to a specific entity. EntityType is part of Entity and there is a one to one relationship. Think of entities as virtual classes, and entity types virtual container definitions for all possible attribute and operational data available to a specific entity.

Consider this example. ExecutiveJobEntityType does not exist as compiled code, but gives us a relative type template for defining an entity's relationships, methods and data. It contains mappings to a number of attributes and methods, so when we add our fifty more classes, we can now map them to a specific type, giving us a generalized template, without the need for sub classing or inheritance. The XML excerpt below shows how the relationships merge. We have attributes and methods, all tied into a nice generalized template. We have two types, based on job level, and from these types we can further define the classes, or entities.

Notice the two XML attributes in the diagram below, attributes and operations. These two attributes contain the name keys to distinguish which operations and attributes are to be associated with the entity types. This is how we link up the different classes and their assemblies, and build our 'parent' definitions. Just think of the meta-data that builds entity type as a template.

 <entityTypes>
   <entityType name="ExecutiveJobEntityType"             
      attributes="FullName,HireDate,IsActive,JobLevel"
      operations="DoSomeWork,GoToLunch,TakeABreak,WriteAReport,ClockOut" />
  <entityType name="EmployeeJobEntityType"             
      attributes="FullName,HireDate,IsActive,JobLevel" 
      operations="DoSomeWork,GoToLunch,TakeABreak,WriteAReport,ClockOut,GoofOff"/>
 </entityTypes>

The actual EntityType class is defined below. The class itself is considered a meta class. It basically acts as a holder for all the relationships, attributes and operational methods we want for a specific template.

        public abstract class EntityType
        {                    
            private string _typeName;
            private Type _type;
            private EntityAttributeCollection _attributes = 
                                      new EntityAttributeCollection();
            private OperationsCollection _operations = 
                                      new OperationsCollection();
        
            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;}                    
            }
    
            public OperationsCollection Operations
            {
                get{return _operations;}
                set{_operations = value;}                    
            }
                    
        }

The Entity class is defined below. The class itself is considered a meta class. This is the class that virtually replaces your EmployeeJob and ExecutiveJob and all fifty class types we mentioned above. It holds a composite of the EntityType class, and has a one to one relationship with that class.

        public class Entity
        {
            private EntityType _entityType;
            
            public Entity(EntityType entityType)
            {
                _entityType = entityType;
                Console.WriteLine("..." + this.GetType().Name + 
                              " initialized with EntityType " +
                              _entityType.Name);            

            }
            
            public EntityType EntityTypeOf
            {
                get{return _entityType;}
            }
        }

The actual classes are not compiled, but instead exist at runtime as virtual meta-classes built from the configuration meta-data. Below we see the XML excerpt that allows us to define how to build this class. If you look at the XML, you will see a type definition, so we know from where we want to grab our particular entity definition, and entity type identifier, and two other XML attributes: extendedAttributes and extendedOperations. These two attributes lets us define specific class definitions based on the entity type template. This replaces the need for inheritance and sub classing, by allowing the meta-data to extend the entity. To the two XML attributes let us add on specific extra attributes and operational strategies (or subtract ones from the current entity, which is out of scope for this example, but perfectly viable for this model).

     <entities>
        <entity name="ExecutiveJob" 
            type="AOMImpl.EntityImpl.Entity" 
            entityType="ExecutiveJobEntityType"
            extendedAttributes="JobLevel"
            extendedOperations="WriteAReport" />
        <entity name="EmployeeJob" 
            type="AOMImpl.EntityImpl.Entity" 
            entityType="EmployeeJobEntityType" 
            extendedAttributes=""
            extendedOperations="WriteAReport,GoofOff" />
    </entities>

The attributes for the entity types and entities work exactly the same way, as far as their grouping goes.

Below we see the definition for EntityAttribute. We see it holds data for its name, the object for the attribute or value, and its Type. This class is also a meta-class, and its actual object data can be loaded from any source, probably a database. But the definition of its type is set up in meta-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;}                    
            }
        }

The meta-data from the configuration file shows us the key name (XML attribute 'name'), the type of the attribute value (XML attribute 'attributeType') and the actual object value (XML attribute attributeValue'), which is not meta-data, and should not be included in your project as such, but is done for this example.

     <entityAttributes>
        <entityAttribute name="FullName" 
            attributeType="System.String" 
            attributeValue="(This is Not META-DATA)" />
        <entityAttribute name="JobLevel" 
            attributeType="System.Int32" 
            attributeValue="1" />
        <entityAttribute name="IsActive" 
            attributeType="System.Boolean" 
            attributeValue="True" />
        <entityAttribute name="HireDate" 
            attributeType="System.DateTime" 
            attributeValue="03/01/2005" />
    </entityAttributes>

Now let's take a look at how the example runs, and go through the flow. When we run the example, we first see that we load up our specific entities loaded with their entity types. This is done in any order in your Interpreter pattern (for this example the order is different than is displayed) that works best to allow the dynamic creation of your classes. Our interpreter is the AOMFactory, which creates and loads our different relationships from the meta-data.

Next we see the specific entities and their operational methods and attribute calls displayed. We do this to illustrate as a test that the expected operations and attributes are part of each class definition. Below we define the EmployeeJob class, and see the methods and attributes we defined in the meta-data for its entity type, as well as its specific methods and attributes are displayed. Notice methods WriteAReport and GoffOff are added to its method definition.

Next we see that ExecutiveJob class, also has the methods and attributes we defined in the meta-data for its entity type, as well as its specific methods and attributes are displayed. Notice method WriteAReport is added to its method definition.

Next we see the ContractorJob class, also the methods and attributes we defined in the meta-data for its entity type, as well as its specific methods and attributes. Since this is a contractor job type it gets only the basic methods.

Meta-Data and Configuration

Now let's talk about who gets to configure the meta-data, and build the applications process flow. Depending on your usages for the code, you might want to allow a power-user, who has some advanced knowledge of the business processes, to modify the meta-data. However you might allow certain pieces of meta-data to be modified by ordinary users. For example, a user might have the ability to build a process flow for his piece of the application, for his particular profile. Suppose one of your business users was a manager, and he needed to build a process that allowed his subordinates to only have certain functionality, and see certain data of an application. He could be allowed to modify the meta-data for his process and build certain methods and attribute types to be displayed on a Web page. He might display data, buttons to certain functions, or other links to the background functionality of the operations, based on his settings in the meta-data.

Project Suggestions

Well, now hopefully I have explained some functional flows and usages of the AOM model, so how does this apply to your projects? The AOM model may not make sense for all projects, but it is a very interesting model nonetheless. Several design types come to mind, including a dynamic content generated web site, a process engine for approvals, etc. The first thing you need to do is to find relevant uses in your project is analyze your current structure and see if it suggests usage of the AOM model. You can do this by seeing if you have any excessive class structures, or maybe some algorithms that are duplicated in several places. Also if you have excessive subclassing, or just want to have an application that is more flexible and responsive to ever changing business requirements.

Another aspect to consider is that the competency level of your development team needs to be rather high for this model to work, and a strong design patterns methodology and mindset needs to be in place in your development group. To use this model, you need to have some knowledge of design pattern methodology, a good understanding of reflection and object typing, and a general knowledge of high level architecture and design. Remember, this is not rocket science, just a model based on reflection, so don't get intimidated. Focus on the pieces, not the whole and you should do fine.

Points of Interest

This is the fourth 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 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.

Other articles in this series include:

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

History

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

Credits

Thanks to:

  • Joe Yoder, Ralph Johnson, for their various articles on the AOM pattern.
  • Martin Fowler for his interest in my articles and his work on the Accountability pattern.
  • Doga Oztuzun, for his insight and sharing of his relevant project data.

    Also to anyone else I have forgotten.

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

Share

About the Author

Christopher G. Lasater
Web Developer
United States United States
Christopher G. Lasater
 
I am also a published author, please check out my book:
ISBN: 1-59822-031-4
Title: Design Patterns
Author:Christopher G. Lasater
More from my web site
Amazon.com


Comments and Discussions

 
GeneralRun-time performance PinmemberBarry Faassen1-May-05 3:48 
GeneralRe: Run-time performance PinsussAnonymous2-May-05 4:23 
GeneralRe: Run-time performance PinmemberBarry Faassen2-May-05 9:13 
GeneralRe: Run-time performance Pinmemberchris lasater3-Jun-05 10:05 
GeneralRe: Run-time performance Pinmemberchris lasater3-Jun-05 10:19 
GeneralThank you PinmemberUnquaLeX25-Apr-05 6:21 
GeneralRe: Thank you Pinmemberchris lasater25-Apr-05 7:02 
GeneralRe: Thank you PinmemberUnquaLeX25-Apr-05 9:20 
GeneralIs the complexity worth it PinsussEdwin Pajemola25-Apr-05 4:15 
GeneralRe: Is the complexity worth it PinsussAnonymous23-Jun-05 16:48 
GeneralVery Good, but... PinsussDavid Kemp20-Apr-05 6:08 
GeneralRe: Very Good, but... PinsussAnonymous20-Apr-05 7:35 
GeneralRe: Very Good, but... PinmemberSean McCormack20-Apr-05 11:33 
GeneralRe: Very Good, but... Pinmemberchris lasater20-Apr-05 12:15 
GeneralRe: Very Good, but... PinsussEdwin Pajemola25-Apr-05 4:26 
GeneralRe: Very Good, but... Pinmemberchris lasater25-Apr-05 5:13 
GeneralRe: Very Good, but... Pinmemberchris lasater17-May-05 10:10 
GeneralRe: Very Good, but... Pinmemberchris lasater17-May-05 10:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141223.1 | Last Updated 21 Jun 2005
Article Copyright 2005 by Christopher G. Lasater
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid