Writing Components Using Self-Describing Business Objects

Problem:
Reflection allows you to write very generic components. Reflection is however implemented at such a low level that it ‘only’ exposes technical aspects thus limiting the use.

Description:
This pattern describes how a business object not only carries methods and properties, but also a ‘descriptor’.
This is a special object that supports a well-defined interface that is able to ‘describe’ the business object. The descriptor can ‘answer’ questions like:

· Name of the object
· Label for the object in English, French or German
· Name of associated data source
· Name of the primary key attribute
· Per attribute the name, label (in different languages), data type, validation rules
· Relationships between self-describing business objects
· Etcetera

These characteristics are stored in an XML configuration file that is read when the descriptor is first created. A single descriptor object for a business object will be shared by all instances of that business object. Changing the XML descriptor file will immediately result in application changes without altering the code.

The business object implementation will not have a native property for each attribute; instead it will have a collection of property objects that is populated at runt-time based on the attributes defined in the descriptor XML.

A very useful extension is a convenient way of referring to a group of attributes: for example:

· Label, the group of attributes that identify the object
· Description, the group that is used to display the result of a query
· Search, the attributes that should appear on a search form
· *, all attributes

This allow for the development of very generic modules. Consider the following example of a module that can serialise a business object to a csv header- and row string (disclaimer: examples have all been stripped down to the bone for clarity):


public String csvHeader(String pstrAttributeGroup) throws ZXException {
StringBuffer csvHeader = new StringBuffer("");

/**
 * Get handle to collection
*/
AttributeCollection colGroup = this.descriptor.getGroup(pstrAttributeGroup);

Iterator iter = colGroup.iterator();
Attribute objAttr;
while (iter.hasNext()) {
objAttr = (Attribute)iter.next();

csvHeader.append("\"");
csvHeader.append(objAttr.getLabel().getLabel());
csvHeader.append("\"");

if(iter.hasNext()) {
csvHeader.append(",");
}
}

return csvHeader.toString();
}

public String csvRow(String pstrAttributeGroup) {
StringBuffer csvRow = new StringBuffer("");

/**
 * Get handle to collection
 */
         AttributeCollection colGroup = this.descriptor.getGroup(pstrAttributeGroup);
        
        Iterator iter = colGroup.iterator();
        Attribute objAttr;
        zXType.dataType objDatatype;
        while (iter.hasNext()) {
            objAttr = (Attribute)iter.next();
            objDatatype = objAttr.getDataType();
            
            csvRow.append("\"");
            csvRow.append(getValue(objAttr.getName()).formattedValue(true));
            csvRow.append("\"");
            
            /**
             * Do not append if there is not more attributes.
             */
            if (iter.hasNext()) {
                csvRow.append(",");
            }
        }
        return csvRow.toString();
        
}

Combining these routines with other generic self-describing object savvy routines allow us to write a dumpToCsv routine:

public String dumpToCsv(String pstrAttributeGroup, String pstrWhereGroup) {
        StringBuffer dumpToCsv = new StringBuffer("");
        
        dumpToCsv.append(csvHeader(pstrGroup));
        
        /**
         * Generic routine that creates collection of business objects
         */
        Iterator iter = db2Collection(pstrAttributeGroup, pstrWhereGroup).iterator();
        ZXBO objBO;
        while (iter.hasNext()) {
            objBO = (ZXBO)iter.next();
            
            dumpToCsv.append(objBO.csvRow(pstrAttributeGroup)).append("\n");
        }
        
        return dumpToCsv.toString();
    }

Now the routine can be used like:

/**
*Create a csv file for all attributes of all clients
**/
file.write(objClient.dumpToCsv(“*”, “status=1”));

/**
 * Create a csv file for the label attributes of all product
 */
file.write(objProduct.dumpToCsv(“label”, “date>#now”));

The pattern can be used to write generic components like:

· Search form generator
· Edit form generator
· Database persistence routines
· Document generation routines
· Serialisation and de-serialisation routines
· Etceteras

For effective use, the pattern requires the following:

· A well-defined interface for the descriptor object
· A repository editor to maintain descriptor files