Chapter 4. Behavioral Patterns

In the preceding chapters, we discussed how we can create objects or structure our code to enhance its modularity and reusability. This chapter focuses on design patterns to enable communication between objects and keep them loosely coupled at the same time.

Introducing these design patterns in code increases their flexibility and reusability in order to carry out the communication between objects. It mostly focuses on how an object interacts and how responsibilities are shared amongst them.

We should consider using behavioral patterns in the following scenarios:

  • Passing same request to multiple handlers
  • Implementing object-oriented callbacks
  • Persisting and recovering the state of an object
  • Parsing languages or defined scripts
  • Sending a state notification to multiple recipients
  • Defining a family of algorithms and using them interchangeably

In this chapter, we will discuss the following design patterns:

  • The chain of responsibility pattern
  • The command pattern
  • The interpreter pattern
  • The iterator pattern
  • The mediator pattern
  • The memento pattern
  • The observer pattern
  • The state pattern
  • The strategy pattern
  • The visitor pattern

The chain of responsibility pattern

It is a common situation where we need to debug or log some important information in Apex. It can help improve error handling and reporting or analyzing runtime errors while customers are using the application.

Apex provides the following debug levels from the lowest to the highest:

  • NONE
  • ERROR
  • WARN
  • INFO
  • DEBUG
  • FINE
  • FINER
  • FINEST

The preceding log levels are cumulative, which means that if the log level is DEBUG, then it will also include ERROR, WARN, and INFO.

Note

There is much more to learn about this topic; however, it would not be possible to cover it entirely in this chapter. Refer to the Salesforce help link to read more about it at  https://help.salesforce.com/apex/HTViewHelpDoc?id=code_setting_debug_log_levels.htm&language=en.

Salesforce provides various debug methods in the System class, as shown in the following code:

//Option 1
 System.debug("Your debug message");

Or you can also use the following code:

//Option 2 
System.debug(Logginglevel," Your debug Message"); 

It is recommended that you use the overridden debug method (option 2) where we can provideLogginglevel parameter as well.  Developers often face problems wherein they do not find the complete log information. This is due to the 2 MB log size limit set by the Force.com platform.

Primarily, developers can configure the debug logging level for Apex, workflow, validation rule, and so on at the user or class level. But, it may not be adequate in some scenarios. For example, when there is a huge code base with lots of debug statements. In such scenarios, defining Logginglevel in debug statements can help you get more granular control of debug logs.

Using debug statements is one of the most basic ways to analyze errors in Apex. Additionally, developers employ other mechanisms such as sending error notifications, saving error information in a custom object, and so on.

Each logging mechanism has its own separate approach to handle error information. It is recommended that we should structure our code to achieve a low cohesion and reusability.

Note

The chain of responsibility pattern focuses on avoiding tight coupling between sender and receiver objects to handle requests. It allows chaining of multiple receiving objects to give a chance to more than one object to handle the same request. It follows the launch and leave design with a single execution path, allowing more than one object to process the request.

The following diagram explains the chain of responsibility pattern at a high level:

The chain of responsibility pattern

Let's try to solve this problem using the chain of responsibility design pattern.

Tip

Sending e-mails via the Apex code can hit the governor limit (1,000 e-mails allowed per 24 hours) if e-mails are sent to an external user. Also, while inserting a log record in a custom object can hit the governor limit (number of DML statements in a single transaction).

Let's create a custom object named Debug to store debug logs. It contains two fields: title and message. The title field is created as an external Id to make it an indexed field.

Note

Marking the field as external Id allows it to be indexed; to improve the performance of related SOQL queries, list view, global search, and reports.

The following screenshot shows the object structure:

The chain of responsibility pattern

We plan to handle debug statements in the following three different ways:

  • Send error notifications
  • Insert a log record in a custom object
  • Add log messages to the standard debug log

As per the single responsibility principle, we will need to create three separate classes for each of the preceding functionalities. Each of these classes will share some common logic, which can be implemented in the base class. This is an opportune moment to create an abstract class.

The abstract class includes the following parameters:

  • Constants to define log levels
  • The setNextLogger method to define the next handler in the chain
  • The logMessage method to decide whether the current and next log handler in the chain needs to be invoked on the basis of the requested log level
  • The write abstract method, which needs to be implemented by all handlers (child classes):
public abstract class RequestHandler{ 
     
    /* constant for debug log */ 
    public static final integer LOG_LEVEL_SYSTEMDEBUG = 1; 
     
    /* constant error and warn */ 
    public static final integer LOG_LEVEL_SAVE = 2; 
     
    /* constant for Send Email */ 
    public static final integer LOG_LEVEL_EMAIL = 3;       
     
    /* Requested log level */ 
    protected integer handlerLogLevel ; 
     
    /* Link to next Request Handler object */ 
    protected RequestHandler nextLogger; 
     
    /* Set next Logger Request Handler class*/ 
    public void setNextLogger(RequestHandler logger){ 
        this.nextLogger = logger; 
    } 
     
    // "Chaining" of log request handlers 
    public void logMessage(integer level, String message){ 
        if(handlerLogLevel <= level){ 
            write(message); 
        }         
        /* "Chain" - Pass request to next Request Handler object*/ 
        if(nextLogger != null){ 
            nextLogger.logMessage(level,message); 
        } 
    } 
    
    //This method needs to be implemented by all child log handlers 
   abstract protected void write(String message);     
} 

Here, we have the first concrete class, which extends the RequestHandler abstract class to display the message in the standard debug logs:

public class RequestHandler_SystemDebug extends RequestHandler{ 
     
    public RequestHandler_SystemDebug(integer log_level){ 
        this.handlerLogLevel = log_level ; 
    } 
     
    /** 
     * This Debug Request Handler class only 
     * log Debug messages in Log Console 
     * */ 
    public override void write(String message){ 
        System.debug(message) ;     
    }  
}  

The second request handler class is used to save messages in a Debug__c custom object so that it can be used later for reporting purposes.

Saving debug records in the database has many pitfalls, which are as follows:

  • It uses the Salesforce data storage
  • Possibilities of hitting DML limits
  • The DML statement cannot be invoked from constructors
public class RequestHandler_Save extends RequestHandler{ 
 
    public RequestHandler_Save(integer log_level){ 
        this.handlerLogLevel = log_level ; 
    } 
     
    /** 
     * This Debug Request Handler class Saves 
     * Debug log record in custom object for 
     * future error analysis. 
     * */ 
    public override void write(String message){ 
         
        //Peform Database operation only if we have DML statement limit available 
        if(Limits.getLimitDMLStatements() - Limits.getDMLStatements() > 1 ) 
        { 
            //Perform Database operation only if we have atleast 1 row limit available for DML 
            if(Limits.getLimitDMLRows() - Limits.getDMLRows() > 1){ 
                 
                /** 
                 * Save first 250 characters in "title" field 
                 * as its indexed , so searching will be fast  
                 * */  
                String title = message.length() > 250 ? message.left(249) : message ; 
                Debug__c debugObj = new Debug__c(Title__c = title, Message__c = message); 
                insert debugObj ; 
            }else{ 
                System.debug(LoggingLevel.ERROR,'DML row limit exhausted'); 
               System.debug(LoggingLevel.ERROR, message); 
            }  
        } 
        else{ 
            System.debug(LoggingLevel.ERROR,'DML statement limit exhausted'); 
            System.debug(LoggingLevel.ERROR, message); 
        }       
         
    } 
}  

The following request handler provides a functionality to send e-mail notifications:

public class RequestHandler_SendEmail extends RequestHandler{ 
      
    public RequestHandler_SendEmail(integer log_level){ 
        this.handlerLogLevel = log_level ; 
    } 
     
    /** 
     * Send Email to Salesforce User. 
     * We would not be sending email to external users to avoid governor limits 
     * */ 
     
    public override void write(String message){ 
        try 
        { 
            List<Messaging.SingleEmailMessage> lstEmailstoSend =  
                  new List<Messaging.SingleEmailMessage>(); 
             
            //read usernames from Custom label which has  
//comma seperated users name 
            for(User u : [SELECT ID FROM User WHERE UserName IN : 
               System.Label.Developer_User_Name.split(',')]){  
 
                Messaging.SingleEmailMessage emailMessage =   
new Messaging.SingleEmailMessage();  
 
               emailMessage.setSubject('Debug Message from Salesforce'); 
 
                emailMessage.setTargetObjectId(u.Id); 
                emailMessage.setPlainTextBody(message);  
                emailMessage.setSaveAsActivity(false); 
                lstEmailstoSend.add(emailMessage);  
            }  
             
            if(!lstEmailstoSend.isEmpty()) 
               Messaging.sendEmail(lstEmailstoSend);  
             
        }catch(Exception e){ 
            System.debug(LoggingLevel.ERROR,'Some error occurred while sending email for Logging purpose'); 
            System.debug(LoggingLevel.ERROR, e.getMessage()); 
        } 
    } 
}  

Now, we need a utility class that will create a chain of all the preceding logger requests that we created earlier, as shown in the following class:

public class Loggers{ 
    
    /** 
     * Below method returns Chained Loggers 
     * */ 
    public static RequestHandler getChainOfLoggers(){ 
         
        RequestHandler debugLogger = new RequestHandler_SystemDebug(RequestHandler.LOG_LEVEL_SYSTEMDEBUG); 
        RequestHandler databaseLogger = new RequestHandler_Save(RequestHandler.LOG_LEVEL_SAVE); 
        RequestHandler emailLogger = new RequestHandler_SendEmail(RequestHandler.LOG_LEVEL_EMAIL); 
         
        //Create Chain of Loggers 
        //DebugLogger -> Database Save -> Send Email 
 
        debugLogger.setNextLogger(databaseLogger); 
        databaseLogger.setNextLogger(emailLogger); 
         
        //return logger which starts chain 
        return debugLogger; 
    } 
}  

The following figure depicts the flow of the chain of responsibility pattern:

The chain of responsibility pattern

The following are the code snippets used to verify a functionality.

Sample code 1:

RequestHandler logger = Loggers.getChainOfLoggers(); 
logger.logMessage(RequestHandler.LOG_LEVEL_SYSTEMDEBUG, 'I am logger using Chain of responsibility principal'); 

Behavior:

The log level in the preceding code snippet is set to LOG_LEVEL_SYSTEMDEBUG (value 1). Therefore, the logMessage method of the RequestHandler abstract class will only execute request handlers that have the log level less than or equal to LOG_LEVEL_SYSTEMDEBUG. In this case, the RequestHandler_SystemDebug handler will be executed.

Output:

  • The message will be printed on the debug log

Sample code 2:

RequestHandler logger = Loggers.getChainOfLoggers(); 
logger.logMessage(RequestHandler. LOG_LEVEL_SAVE, 'I am logger using Chain of responsibility principal'); 

Behavior:

The log level in the preceding code snippet is set to LOG_LEVEL_SAVE (value 2). In this case, the RequestHandler_SystemDebug and RequestHandler_Save handlers will be executed.

 

Output:

  • The message will be printed on the debug log 
  • The message will be saved in a Debug__c custom object

Sample code 3:

RequestHandler logger = Loggers.getChainOfLoggers(); 
logger.logMessage(RequestHandler. LOG_LEVEL_EMAIL, 'I am logger using Chain of responsibility principal'); 

Behavior:

The log level in the preceding code snippet is set to LOG_LEVEL_EMAIL (value 3). In this case, the RequestHandler_SystemDebug, RequestHandler_Save, andRequestHandler_SendEmail handlers will be executed.

Output:

  • The message will be printed on the debug log 
  • The message will be saved in a Debug__c custom object
  • An e-mail will be sent to all users saved in the custom label

The following diagram is a class diagram for our final code:

The chain of responsibility pattern

As discussed in the previous design pattern, we learnt a new way to decouple a request and a series of receiver objects, and at the same time recursively call all request handlers, which know  how to process a request. This is very powerful and one of the most common behavioral design patterns used.

Points to consider

  • The chain of responsibility addresses how you can decouple senders and receivers
  • The chain of responsibility passes a sender request along with a chain of potential receivers
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset