This Blog describes how to leverage common Apex Design Patterns to optimize your code and ensure reusability, maintainability and performance.
Common Design Patterns
The following are a list of design patterns, some of which are standard object-oriented patterns in a Force.com context, and some of which are specific Force.com patterns.
- Singleton - minimizing object instantiation for improved performance and to mitigate impact of governor limits
- Strategy - defining a family of algorithms, enscapsulating each one and making them interchangeable and selectable at runtime
- Decorator - extending the functionality of an sObject in Apex
- Facade - simplifying the execution of classes with complex interfaces (e.g. web service callouts)
- Composite - treating a group of objects in a similar manner to a single instance of that object
- Bulk State Transition - efficiently tracking the change of a field value in a trigger and executing functionality based on this change
Singleton:
The Singleton pattern attempts to solve the issue of repeatedly using an object instance, but only wishing to instantiate it
once within a single transaction context.
Sample Code:-
trigger AccountTrigger on Account (before insert, before update) {
for(Account record : Trigger.new){
// Instantiate the record type using the singleton class
AccountFooRecordType rt = AccountFooRecordType.getInstance();
....
}
}
public class AccountFooRecordType {
// private static variable referencing the class
private static AccountFooRecordType instance = null;
public String id {get;private set;} // the id of the record type
// The constructor is private and initializes the id of the record type
private AccountFooRecordType(){
id = Account.sObjectType.getDescribe()
.getRecordTypeInfosByName().get('Foo').getRecordTypeId();
}
// a static method that returns the instance of the record type
public static AccountFooRecordType getInstance(){
// lazy load the record type - only initialize if it doesn't already exist
if(instance == null) instance = new AccountFooRecordType();
return instance;
}
}
The above code demonstrates the following:
- The getInstance() static method will only instantiate an instance of the class if it doesn't already exist in a lazy-initialization manner
- The constructor and the instance variable for the class is private to make sure that it cannot be instantiated outside of the getInstance() method
- The class defines a private, static instance of itself that can only be referenced via the getInstance() static method
Strategy:
Defining a family of algorithms, enscapsulating each one and making them interchangeable and selectable at runtime.Sample Code:
1. Create an interface.
public interface Strategy {
Integer doOperation(Integer num1, Integer num2);
}
2. Create concrete classes implementing the same interface.
public class OperationAdd implements Strategy {
public Integer doOperation(Integer num1, Integer num2) {
return num1 + num2;
}
}
public class OperationSubstract implements Strategy {
public Integer doOperation(Integer num1, Integer num2) {
return num1 - num2;
}
}
3. Create Context Class.
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public Integer executeStrategy(Integer num1, Integer num2) {
return strategy.doOperation(num1, num2);
}
}
4. Use the Context to see change in behaviour when it changes its Strategy.
Context context = new Context(new OperationAdd());
System.debug('10 + 5 = ' + context.executeStrategy(10, 5));
context = new Context(new OperationSubstract());
System.debug('10 - 5 = ' + context.executeStrategy(10, 5));
Decorator:
Extending the functionality of an sObject in Apex.
Sample Wrapper Class using Apex in Salesforce
Sample Code:
Visualforce Page:
<apex:page controller="Sample" tabStyle="Account">
<apex:form >
<apex:pageMessages />
<apex:pageBlock id="pg">
<apex:pageBlockSection >
<apex:pageBlockTable value="{!listWrapper}" var="a">
<apex:column >
<apex:inputCheckbox value="{!a.checkBool}"/>
</apex:column>
<apex:column value="{!a.acct.Name}"/>
<apex:column value="{!a.acct.Industry}"/>
</apex:pageBlockTable>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</apex:page>
Apex Controller:
public class Sample {
public List<WrapperClass> listWrapper {get;set;}
public Sample() {
List<Account> listAcct = [SELECT Name, Industry FROM Account LIMIT 1000];
if(listAcct.size() > 0) {
listWrapper = new List<WrapperClass>();
for(Account a : listAcct) {
listWrapper.add(new WrapperClass(a));
}
}
}
public class WrapperClass {
public Boolean checkBool {get;set;}
public Account acct {get;set;}
public WrapperClass(Account acct) {
this.acct = acct;
}
}
}

Visualforce Page:
<apex:page controller="Sample" tabStyle="Account">
<apex:form >
<apex:pageMessages />
<apex:pageBlock id="pg">
<apex:pageBlockSection >
<apex:pageBlockTable value="{!listWrapper}" var="a">
<apex:column >
<apex:inputCheckbox value="{!a.checkBool}"/>
</apex:column>
<apex:column value="{!a.acct.Name}"/>
<apex:column value="{!a.acct.Industry}"/>
</apex:pageBlockTable>
</apex:pageBlockSection>
</apex:pageBlock>
</apex:form>
</apex:page>
Apex Controller:
public class Sample {
public List<WrapperClass> listWrapper {get;set;}
public Sample() {
List<Account> listAcct = [SELECT Name, Industry FROM Account LIMIT 1000];
if(listAcct.size() > 0) {
listWrapper = new List<WrapperClass>();
for(Account a : listAcct) {
listWrapper.add(new WrapperClass(a));
}
}
}
public class WrapperClass {
public Boolean checkBool {get;set;}
public Account acct {get;set;}
public WrapperClass(Account acct) {
this.acct = acct;
}
}
}

Facade:
Simplifying the execution of classes with complex interfaces (e.g. web service callouts).
Sample Code:
/* Complex parts */
class CPU {
public void freeze() { ... }
public void jump(long position) { ... }
public void execute() { ... }
}
class HardDrive {
public byte[] read(long lba, int size) { ... }
}
class Memory {
public void load(long position, byte[] data) { ... }
}
/* Facade */
class ComputerFacade {
private CPU processor;
private Memory ram;
private HardDrive hd;
public ComputerFacade() {
this.processor = new CPU();
this.ram = new Memory();
this.hd = new HardDrive();
}
public void start() {
processor.freeze();
ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE));
processor.jump(BOOT_ADDRESS);
processor.execute();
}
}
/* Client */
class You {
public static void main(String[] args) {
ComputerFacade computer = new ComputerFacade();
computer.start();
}
}
Composite:
Treating a group of objects in a similar manner to a single instance of that object. Composite pattern is used where we need to treat a group of objects in similar way as a single object. Composite pattern composes objects in term of a tree structure to represent part as well as whole hierarchy. This type of design pattern comes under structural pattern as this pattern creates a tree structure of group of objects.
Sample Code:
Apex Class:
public class Employee {
private String name;
private String dept;
private Integer salary;
private List<Employee> subordinates;
// constructor
public Employee(String name,String dept, Integer sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
subordinates = new List < Employee >();
}
public void add(Employee e) {
subordinates.add(e);
}
public List<Employee> getSubordinates() {
return subordinates;
}
}
Execute the below code to set the hierarchy and view the debug log.
Employee CEO = new Employee('John','CEO', 30000);
Employee headSales = new Employee('Robert','Head Sales', 20000);
Employee headMarketing = new Employee('Michel','Head Marketing', 20000);
Employee clerk1 = new Employee('Laura','Marketing', 10000);
Employee clerk2 = new Employee('Bob','Marketing', 10000);
Employee salesExecutive1 = new Employee('Richard','Sales', 10000);
Employee salesExecutive2 = new Employee('Rob','Sales', 10000);
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
//print all employees of the organization
System.debug(CEO);
for ( Employee headEmployee : CEO.getSubordinates() ) {
System.debug(headEmployee);
for ( Employee employee : headEmployee.getSubordinates() ) {
System.debug(employee);
}
}
Simplifying the execution of classes with complex interfaces (e.g. web service callouts).
Sample Code:
/* Complex parts */
class CPU {
public void freeze() { ... }
public void jump(long position) { ... }
public void execute() { ... }
}
class HardDrive {
public byte[] read(long lba, int size) { ... }
}
class Memory {
public void load(long position, byte[] data) { ... }
}
/* Facade */
class ComputerFacade {
private CPU processor;
private Memory ram;
private HardDrive hd;
public ComputerFacade() {
this.processor = new CPU();
this.ram = new Memory();
this.hd = new HardDrive();
}
public void start() {
processor.freeze();
ram.load(BOOT_ADDRESS, hd.read(BOOT_SECTOR, SECTOR_SIZE));
processor.jump(BOOT_ADDRESS);
processor.execute();
}
}
/* Client */
class You {
public static void main(String[] args) {
ComputerFacade computer = new ComputerFacade();
computer.start();
}
}
Composite:
Treating a group of objects in a similar manner to a single instance of that object. Composite pattern is used where we need to treat a group of objects in similar way as a single object. Composite pattern composes objects in term of a tree structure to represent part as well as whole hierarchy. This type of design pattern comes under structural pattern as this pattern creates a tree structure of group of objects.
Sample Code:
Apex Class:
public class Employee {
private String name;
private String dept;
private Integer salary;
private List<Employee> subordinates;
// constructor
public Employee(String name,String dept, Integer sal) {
this.name = name;
this.dept = dept;
this.salary = sal;
subordinates = new List < Employee >();
}
public void add(Employee e) {
subordinates.add(e);
}
public List<Employee> getSubordinates() {
return subordinates;
}
}
Execute the below code to set the hierarchy and view the debug log.
Employee CEO = new Employee('John','CEO', 30000);
Employee headSales = new Employee('Robert','Head Sales', 20000);
Employee headMarketing = new Employee('Michel','Head Marketing', 20000);
Employee clerk1 = new Employee('Laura','Marketing', 10000);
Employee clerk2 = new Employee('Bob','Marketing', 10000);
Employee salesExecutive1 = new Employee('Richard','Sales', 10000);
Employee salesExecutive2 = new Employee('Rob','Sales', 10000);
CEO.add(headSales);
CEO.add(headMarketing);
headSales.add(salesExecutive1);
headSales.add(salesExecutive2);
headMarketing.add(clerk1);
headMarketing.add(clerk2);
//print all employees of the organization
System.debug(CEO);
for ( Employee headEmployee : CEO.getSubordinates() ) {
System.debug(headEmployee);
for ( Employee employee : headEmployee.getSubordinates() ) {
System.debug(employee);
}
}
Related Link
No comments:
Post a Comment