# Friday, 06 September 2013

Not to be confused with "rapper" class.

The concept of a "wrapper class" occurs frequently in Force.com development. Object oriented design encourages the use of classes to encapsulate data and behaviors that may mutate the data, or interact with other classes.

The goals of an Apex wrapper class are:

  • Decouple data (records) from behaviors (methods)
  • Validate inputs
  • Minimize side effects
  • Enable unit testing
  • Handle exceptions

This article provides an example wrapper class template to address these goals and explores each aspect of the wrapper class individually.

Register for Dreamforce 13 and attend my session on "Leap: An Apex Development Framework" for training on how to use the Leap framework to generate wrapper classes, triggers, and more. Follow @codewithleap on Twitter for updates on Apex design patterns.

All Apex wrapper classes share a few common properties and methods. To avoid repeatedly cut-n-pasting the same common code into each wrapper class, a base class is created for all wrapper classes to inherit through the "extends" keyword.

public abstract class WrapperClassBase {
     public Id id;  
     public boolean success = true;
     public List<String> errors = new List<String>();
     public boolean hasErrors(){ return errors.size() > 0;}
     public void clearErrors() { success = true; errors.clear();}
}

Now, with a base class available, the simple wrapper class looks like the following:

public with sharing class Order extends WrapperClassBase {
     public Order__c record   = null;
     public static final String SFIELDS = 'Id, Name'; // ... add other fields here
     
     public Order(){}
     
     public Order withSObject(Order__c orderRecord){
          this.record = orderRecord;
          this.id = orderRecord.Id;
          return this;
     }
     
     public Order withId(ID recordId){
          id = recordId;
          String sFieldsToQuery = Order.SFIELDS;
          record = Database.query('SELECT ' + Order.SFIELDS + ' FROM Order__c WHERE Id=:id LIMIT 1');
          return this;
     }
     
     public Order validate(){         
          /*
          Validate fields here. Set this.success = false and populate this.errors.add('err message');
          */
          return this;
     }
     
     public Order doSomething(){
          return this;
     }
     
     private Map<ID,LineItem> m_lineItems = null;
     public Map<Id,LineItem> lineItems{
          get{
               if(m_lineItems == null){
                    m_lineItems = LineItem.fromRecords( this.lineItemRecords );
               }
               return m_lineItems;
          }
     }
     
     private List<OrderLineItem__c> m_lineItemRecords = null;
     public List<OrderLineItem__c> lineItemRecords{
          get{
               if(m_lineItemRecords == null){
                    m_lineItemRecords = Database.query('SELECT ' + LineItem.SFIELDS + ' FROM OrderLineItem__c WHERE Order__c=:id');
               }
               return m_lineItemRecords;
          }
     }
     
     public static Map<ID,Order> fromRecords(List<Order__c> records){
          Map<ID,Order> orders = new Map<ID,Order>();
          for(Order__c o : records){
               orders.put(o.Id, new Order().withSObject(o));
          }
          return orders;
     }
     
     public String toJSON(){
          Map<String, String> r = new Map<String, String>();
          List<String> fieldNames = Order.SFIELDS.split(',');
          for(String fName : fieldNames){
               r.put(fName, String.valueOf( this.record.get(fName) ));
          }
          return JSON.serialize(r);
     }
}

Inheritance

As of this writing, Apex does not allow inheriting the core SObject class (which would be ideal).

Record encapsulation uses the strongly typed Order__c record, rather than the abstractly typed SObject, in order to use the benefits of strongly typed objects in the Force IDE, such as auto-complete of the fields. Moving the record to the base class would require constantly casting SObject to the strong type.

Class Name

For custom objects, it's common to remove the namespace prefix and __c suffix from the wrapper class name. For standard objects, always prefix the class name with "SFDC", or some other naming convention, to avoid conflicts.

Wrapping Standard Objects

Creating wrapper classes with the same name as standard objects, although possible, is discouraged. Class names supersede standard object names, such that if the intent is to create a standard Account object, but a class named 'Account' already exists, the code will not compile because the compiler is trying to create an instance of the wrapper class and not the standard object.

To get around this, use a standard naming convention; SFDCAccount, SFDCContact, or SFDCLead; to differentiate the wrapper class names from their respective standard objects.

Construction

SObjects are constructed in 2 contexts:

  • withSObject(SObject record): The record has already been retrieved via a SOQL statement and the data just needs to be wrapped in a class container.
  • withId(ID recordId): The ID of a record is known, but has not yet been retrieved from the database.
The actual class constructor accepts no arguments. The builder pattern is used to construct the class and kick off a fluent chain of subsequent methods in a single line.

Once constructed, SObject fields are accessed directly through the public 'record' property, as in:

new Order().withID(someid).record.Custom_Field__c

This convention is chosen over creating getCustomField() and setCustomField() properties for brevity and to make use of code auto-complete features,. However, if mutability of the SObject record, or it's fields, are a concern then the public accessor can be modified to 'private' and corresponding get/set properties added.

SFIELDS

Each wrapper class exposes a static public string named SFIELDS for use in selecting all fields for a record. This is equivalent to writing "SELECT * FROM TABLE_NAME" in traditional SQL syntax.

The SFIELDS string can be periodically auto-generated from a Leap task to keep wrapper class field definitions in sync with the data model, or manually updated with just a subset of fields to be used by the wrapper class.

Builder Pattern

The real magic in the wrapper class template is it's ability to chain several methods together in a single line, commonly referred to as the Builder Pattern and discussed in a previous article, Developing Fluent Interfaces With Apex.

Using the Order__c wrapper class example above, the following is possible:

     Order o = new Order().withSObject(objectList.get(0)).doSomething();
     if(o.validate().hasErrors()){
          //handle exceptions
     }

The return types of builder methods must be the same as the wrapper class type. Each builder method returns 'this' to allow method chaining. Builder pattern is useful in the early stages of development when the exact method behaviors and system architecture is not entirely known (see 40-70 Rule to Technical Architecture) and allows a compositional flow to development, incrementally adding new features without significant refactoring effort.

Child Object Relationships

A wrapper class represents a single instance of a Salesforce record. Depending on how lookup relationships are defined, wrapper classes will usually be either a parent (master) or child (detail) of some other records, which also have wrapper classes defined.

The "fromRecords" utility method is provided to easily construct collections of child objects retrieved from SOQL queries. Collections of child wrapper classes are stored as Maps that support the quick lookup of child wrapper classes by their record ID.

Properties and Side Effects

The #1 cause of software entropy in Apex development is unwanted "side effects", which is the dependency on class variables that can be modified by other methods.

The wrapper class template encourages lazy initialization of properties to protect access to member variable. Lazy initialization also avoids repeated queries for the same records, which is a common cause for exceeding governor limits.

Java has not yet evolved to support class properties. But Apex does, and wrapper classes are an opportunity to use them whenever possible. For the sake of brevity, properties are preferred over methods, whenever possible. This Microsoft .NET article on choosing between properties and methods is very applicable to Apex.

For Developers doing a lot of client-side Javascript development in the UI, the use of server-side Apex properties closely approximates the associative array behavior of Javascript objects, and maintains a consistent coding style across code bases.

Unit Testing

Wrapper classes provide a clean interface for unit testing behaviors on objects. The Winter '14 release requires that unit tests be managed in a separate file from the wrapper class. Common convention is to always create a unit test file for each wrapper class with a suffix of 'Tests' in the class name.

Exception Handling

Without a clear exception handling strategy, it can be confusing for Developers to know how a class handles exceptions. Does it consistently bubble up, or catch all exceptions? There is no equivalent to the Java 'throws' keyword in Apex. To remedy this, the wrapper class template base provides a boolean 'success' flag that can be set by any method at any time.

When setting success=false, the exception handling code should also add to the List errors collection, giving an explanation of what went wrong in the transaction. It is the responsibility of calling objects/methods to check for success or hasErrors() after any transaction.

JSON Serialization

Wrapper classes can be serialized to JSON and returned to requesting clients for use in UI binding. The ToJSON() method is provided in the wrapper class template and can be customized to serialize the class.

Comments are closed.