Design Patterns In Salesforce

Introduction

Design Patterns are mainly used to make our code work efficiently and effectively. It also helps to prevent hitting any governor limits.

What is a Design Pattern?

Design Pattern describes a problem that repeatedly occurs, the core, and the consequences of a solution. It doesn’t provide actual implementation details.

Different types of Design Patterns in Salesforce

Singleton Pattern in Salesforce

It’s one of the creational design patterns in which a single instance of a class can be created per transaction. The reason is due to the repeated execution of an instance of a class in a single transaction, there are high chances to get the Limit Exception. This pattern works to reduce the repeated instantiation of a class.

Below are some scenarios:

1. Application Configuration Reader

2. Utility Class (like Math)

3. Connection with Database

4. Logger

Example:

Problem with a code

trigger AccountTrigger on Account (before insert, before update){
for(Account accRec : Trigger.new){
AccountExampleRecordType aert = new AccountExampleRecordType();
accRec.recordTypeId = aert.Id;
}
}
public class AccountExampleRecordType {
public String Id {get; private set;}
public AccountExampleRecordType(){
Id = Account.sObjectType.getDescribe().getRecordTypeInfosByName()
.get('Example').getRecordTypeId();
}
}

 

The issue in the above code is that if multiple records are inserted or updated, then we will get the governor limit problem as the ‘AccountExampleRecordType’ object is created each time for each record.

Solution: Singleton Pattern

Below is the ‘AccountExampleRecordType’ handler class implemented with Singleton Pattern.

public class AccountExampleRecordType {
//referencing the class
private static AccountExampleRecordType instance = null;
//Record Type Id
public String Id {get; private set;}


//Private Constructor initializes the id of the record type
private AccountExampleRecordType(){
Id = Account.sObjectType.getDescribe().getRecordTypeInfosByName()
.get('Example').getRecordTypeId();
}


//a static method returning the instance of record type
public static AccountExampleRecordType getInstance(){
//Lazy loading variant
if(instance == null){
instance = new AccountExampleRecordType();
return instance;
}
}
}

 

Decorative Pattern in Salesforce

It is a part of the Structural design pattern. It is used to provide additional functionality to the sObject using Apex programming in Salesforce.

Example:

public class ExpenseClassDecorator {
public List convertedExpenses{set; get;}
//constructor
public ExpenseClassDecorator(){
List expenses = [SELECT Id, Name, Amount__c
FROM Expense__c];
if(expenses.size() > 0){
convertedExpenses = new List();
for(Expense__c expense : expenses){
convertedExpenses.add(new ExpensesConverted(expense));
}
}
}
}
public class ExpensesConverted{
Double dollarToInr = 79.85;
Double dollarToEuro = 0.98
Double dollarToChf = 0.97;


public String expenseName {get; set;}
public Double expInDollar {get; set;}
public Double expInInr {get; set;}
public Double expInEuro {get; set;}
public Double expInChf {get; set;}


public ExpensesConverted(Expense__c expense){
expenseName = expense.Name;
expInDollar = expense.Amount__c;
expInInr = expInDollar * dollarToInr;
expInEuro = expInDollar * dollarToEuro;
expInChf = expInDollar * dollarToChf;
}
}

 

VisualForce Page to show all the expenses

<apex:page controller = "ExpenseClassDecorator">
<apex:form>
<apex:pageBlock title = "Expenses">
<apex:pageBlockTable value = "{!convertedExpenses}" var = "eachConvertedExpenses">
<apex:column headerValue = "Expense Name" value = "{!eachConvertedExpenses.expenseName}" />
<apex:column headerValue = "Expense In Dollar" value = "{!eachConvertedExpenses.expInDollar}" />
<apex:column headerValue = "Expense In Inr" value = "{!eachConvertedExpenses.expInInr}" />
<apex:column headerValue = "Expense In Euro" value = "{!eachConvertedExpenses.expInEuro}" />
<apex:column headerValue = "Expense In Chf" value = "{!eachConvertedExpenses.expInChf}" />
</apex:pageBlockTable>
</apex:pageBlock>
</apex:form>
</apex:page>

 

iTrigger Factory Pattern in Salesforce

This design pattern helps normalize the trigger and control the execution flow. It makes unit testing much simpler by eliminating the trigger rationale and helps a group of people to work in an organization with multiple triggers.

Example:

public interface ITriggerPattern{
void BeforeInsert(List newRecords);
void BeforeUpdate(Map newRec, Map oldRec);
void BeforeDelete(Map oldRecords);
void AfterInsert(Map newRecords);
void AfterUpdate(Map newRecords, Map(oldRecords));
void AfterDelete(Map oldRecords);
void AfterUndelete(Map oldRecords);
Boolean IsDisabled();
}
public class TriggerClass{
public static void demo(ITriggerPattern pattHandler){
if(Trigger.isBefore){
if(Trigger.isInsert) pattHandler.BeforeInsert(trigger.new);
if(Trigger.isUpdate) pattHandler.BeforeUpdate(trigger.newMap, trigger.oldMap);
if(Trigger.isDelete) pattHandler.BeforeDelete(trigger.oldMap);
}


if(Trigger.isAfter){
if(Trigger.isInsert) pattHandler.AfterInsert(trigger.newMap);
if(Trigger.isUpdate) pattHandler.AfterUpdate(trigger.newMap, trigger.oldMap);
if(Trigger.isDelete) pattHandler.AfterDelete(trigger.oldMap);
if(Trigger.isUnDelete) pattHandler.AfterDelete(trigger.oldMap);
}
}
}

 

Strategy Pattern in Salesforce

It has a behavioral design pattern that allows us to define a family of algorithms making them selectable and interchangeable at runtime.

Example:

public Interface paymentGateway{
Boolean pay(Double amt);
}
public class creditCard implements paymentGateway{
String name;
String cardNumb;
String cvv;
Date doe;


public Boolean pay(Double amt){
//Credit card Interface using Rest API
}
}
public class mQuickPaymentStrategy implements paymentGateway{
String emailId;
public Boolean pay(Double amt){
//Mobikwik interface using Rest API
}
}

 

“One Trigger per Object” Design Pattern in Salesforce

It is highly recommended by Salesforce and also a good practice to write a single trigger on each object because if we write multiple triggers on each object, conflict, and errors can occur since there is no order of execution in the trigger.

We can make use of the context variables to invoke different methods from the helper class.

Example:

trigger AccountTrigger on Account (after insert, after update){
if(Trigger.isAfter && Trigger.isInsert){
UpdateCase.caseMethod(Trigger.new);
UpdateOpportunity.oppMethod(Trigger.new);
}
}

 

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.