Contents

Introduction

This article describes how to implement a Decorator design pattern in Apex. The Decorator pattern attempts to solve the issue where you need temporary fields for processing (typically in Visualforce) but do not need to add these fields to the sObject.

Common uses for this pattern include:

  • Selection Checkbox – e.g. a list of records that the user selects and applies behavior to. The selection checkbox is not saved.
  • Calculated fields – for example, a complex read-only value that cannot be easily done in a formula field (e.g. calculation of a check digit)
  • Proxy fields – for example, a field that when updated, will convert to a different value on the record (e.g. temperature figures presented to the user in C but stored as F)

This pattern is primarily for Visualforce use cases.

For the parent page to this article, and for a full list of other design patterns, go to Apex Design Patterns.

Problem

You need to obtain or display temporary information on a Visualforce page which is not needed beyond the context of the interaction.

UML

Apex Design Patterns Decorator.png

Implementation

In order to implement the Decorator pattern in Apex we need to be aware that this is not a true OO implementation, but the intent is to add behavior at runtime rather than via inheritance at compile time. It is implemented by:

  • Understanding the existing sObject superclass – the actual sObject class!
  • Understanding the existing concrete sObject – our underlying sObject (e.g. Account, Opportunity) – this class is not extensible in Apex
  • Creating a "sObject Decorator" class that wraps the sObject with a pointer to the concrete sObject with additional operations and properties that extend the behavior of the concrete sObject at runtime
  • Creating a Visualforce controller/class that acts as a client to the decorator

Note that this is not a “true” implementation of the OO Decorator pattern:

  • The decorator class does not implement an interface to sObject (you can’t do it!)
  • The sObject pointer in the class is public to simplify access to its behaviors (unless you want to recreate all the sObject methods in the class)
  • It does have the intent of decorator, which is to add functionality at runtime

In our example scenario we have a Weather sObject with City__c and TempInFahrenheit__c fields and our Visualforce requirements are:

  • the stored temperature in Fahrenheit should be displayed in Celsius
  • when the user enters the temperature in Celsius it is stored as Fahrenheit

Decorator Weather Example.png

The Code - Decorated sObject Class

public class DecoratedWeather {

  public Weather__c weather { get; private set; }

  public DecoratedWeather (Weather__c weather) {
            this.weather = weather;  
  }

  public Decimal tempInCelcius { 
    get {
      if (weather != null && tempInCelcius == null )
        tempInCelcius = new Temperature().FtoC(weather.TempInFahrenheit__c);
                    
      return tempInCelcius;  
    }   
    set {
      if (weather != null && value != null ) 
        weather.TempInFahrenheit__c= new Temperature().CtoF(value);

            tempInCelcius = value;
    }
  }
}

The above code demonstrates how the decorator class extends or wraps the Weather sObject with new functionality.

  • Reference the weather sObject as a public property, but make it private set. (This is different to the true decorator pattern, which makes this private and delegates behavior instead)
  • Constructor – is passed in the weather object
  • Property for tempInCelcius
  • Getter – converts the temp from F to C and returns it (assuming we have another class to conver temperature)
  • Setter – uses the value entered to convert from C to F and a side effect to store into the weather.tempInF. The last line makes sure that the value is stored back on the property.

The Code - Custom Controller

public class Weather_Controller {

  public List<DecoratedWeather> listOfWeather {

    get {
      if (listOfWeather == null) {
        listOfWeather = new List<DecoratedWeather>();

        for (Weather__c weather : [select name, temperature__c from Weather__c]) {        
            listOfWeather.add(new DecoratedWeather(weather));
        }
      }
      return listOfWeather;
    }

    private set;
  }
}

The above code demonstrates how the Weather Controller is the client

  • Has a property that is a list of DecoratedWeather (our wrapper class)
  • Has a getter that lazy initialises the list by selecting from the Weather__c table
  • Private setter – only this class can set it

The Code - Visualforce Page

<apex:page controller="weather_controller">
  <!-- VF page to render the weather records with Ajax that only rerenders 
     the page on change of the temperature
  -->
  <apex:form id="theForm">
    <apex:pageBlock id="pageBlock">
      <apex:pageBlockTable value="{!listOfWeather}" var="weather">
        <apex:column value="{!weather.weather.name}"/>
        <apex:column headerValue="Temperature (C)">
          <apex:actionRegion >
            <apex:inputText value="{!weather.tempInCelcius}">
              <apex:actionSupport event="onchange"
                      reRender="pageBlock"/>
            </apex:inputText>
          </apex:actionRegion>
        </apex:column>
        <apex:column headerValue="Temperature (F)"
              value="{!weather.weather.Temperature__c}"
              id="tempInF"/>
      </apex:pageBlockTable>
    </apex:pageBlock>
  </apex:form>
</apex:page>

The above code demonstrates that the Visualforce page only needs to implement a little ajax action support to rerender the page if the temperature changes. There is no real client side logic and no other fancy tricks, just a simple rerender. The getter and setter in the decorator class takes care of all the work!

Conclusion

So what we have is an example of how to extend sObject functionality in Apex with behavior at runtime rather than through inheritance using a pseudo decorator pattern. Most of you would have seen this done with selection checkboxes, but it’s something that can be definitely applied with other use-cases as well.

See the Apex Design Patterns article for more design patterns.