What is Decorator Pattern?: Decorator pattern allows a user to add new functionality to an existing object without altering its structure. This type of design pattern comes under structural pattern as this pattern acts as a wrapper to existing class.
This pattern creates a decorator class which wraps the original class and provides additional functionality keeping class methods signature intact.
Note: this is not a “true” implementation of the OO Decorator pattern.
Why Decorator Pattern?: You may want to use decorator pattern because of following reasons-
- Sometimes you may want selection checkbox whose value you don’t want to be saved in database.
- Sometimes you may want complex calculated field to be displayed on the page which is not possible through formula.
- Sometimes you may want to proxy field, which when updated, is converted to a different value on the record.
Problem: You need to obtain or display temporary information on a Visualforce page that is not needed beyond the context of the interaction.
Solution: We solve it using decorator pattern which extends the sObject functionality. Let’s solve it with an example.
Example: We will be using custom weather object which has temperature field which is Fahrenheit. Using decorator pattern, we will displaying this value in Celsius on VF page. Also we will be changing value in celsius temperature value, which will be reflect the converted Fahrenheit value to temperature field on weather object.
public class TemperatureConverter {
// Takes a Fahrenheit temperature and returns the Celsius equivalent.
public static Decimal FahrenheitToCelsius(Decimal fh) {
Decimal cs = (fh - 32) * 5/9;
return cs.setScale(2);
}
// Takes a Celsius temperature and returns the Fahrenheit equivalent.
public static Decimal CelsiusToFahrenheit(Decimal cs) {
Decimal fh = cs*9/5 + 32;
return fh.setScale(2);
}
}
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 = TemperatureConverter.FahrenheitToCelsius(weather.temperature__c);
}
return tempInCelcius;
}
set {
if (weather != null && value != null) {
weather.temperature__c = TemperatureConverter.CelsiusToFahrenheit(value);
}
tempInCelcius = value;
}
}
}
public class WeatherController {
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; //only this class can set this
}
}
<apex:page controller="WeatherController ">
<!-- 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" Title='Weather POC by Sandeep'>
<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>
Preview:
Most of you will have seen this done with selection checkboxes, but it’s something that can be applied with other use-cases as well.
For all Design Patterns, please refer this.