Chapter 2. Creational Patterns

Apex programming revolves around objects and classes. During the course of application development, we create multiple classes. Also, we create objects of these classes to satisfy business requirements. Creating an object of a class within another class, creates dependency between those classes. This is also known as tight coupling.

As discussed in the previous chapter, tight coupling is considered inferior, as it can negatively impact the code's maintainability and scalability. Creational design patterns can help us avoid tight coupling by hiding the object creational logic.

Creational design patterns can be considered in the following scenarios:

  • Instantiating classes in the Apex Scheduler class. If we directly instantiate the Apex class in scheduler using new keyword, then the class gets serialized and, therefore, locked for further changes.
  • Using multiple classes while creating test data in test classes.
  • Creating code libraries only to reveal their usage and not their actual implementation.
  • Dynamically creating instances of classes based on the configuration stored in custom settings.

In this chapter, we will discuss some problems that can occur mainly during the creation of class instances and how we can write the code for the creation of objects in a more simple, easy to maintain, and scalable way.

We will discuss the following creational design patterns:

  • The factory method pattern
  • The abstract factory pattern
  • The builder pattern
  • The prototype pattern
  • The singleton pattern

Factory method pattern

Often, we find that some classes have common features (behavior) and can be considered classes of the same family. For example, multiple payment classes represent a family of payment services. Credit card, debit card, and net banking are some of the examples of payment classes that have common methods, such as makePaymentauthorizePayment, and so on. Using the factory method pattern, we can develop controller classes, which can use these payment services, without knowing the actual payment type at design time.

Note

The factory method pattern is a creational design pattern used to create objects of classes from the same family without knowing the exact class name at design time.

Using the factory method pattern, classes can be instantiated from the common factory method. The advantage of using this pattern is that it delegates the creation of an object to another class and provides a good level of abstraction.

Let's learn this pattern using the following example:

The Universal Call Center company is a new business and provides free admin support to customers for resolving issues related to their products. A call center agent can get some information about the available product support; for example, to get the Service Level Agreement (SLA) or information about the total number of open tickets allowed per month.

A developer came up with the following class:

public class AdminBasicSupport{
  /** 
   *  return SLA in hours 
   */            
   public Integer getSLA() 
   { 
     return 40; 
   } 
  /** 
   * Total allowed support tickets allowed every month 
   */ 
   public Integer allowedTickets(){ 
     // As this is basic support 
     return 9999; 
   } 
} 

Now, to get the SLA of AdminBasicSupport, we need to use the following code every time:

AdminBasicSupport support = new AdminBasicSupport(); 
System.debug('Support SLA is - '+support.getSLA()); 
 
Output - Support SLA is - 40 

The Universal Call Centre company was doing very well, and in order to grow the business and increase the profit, they started premium support for customers who were willing to pay for cases and get quick support. To make it special and distinct from the basic support, they changed the SLA to 12 hours and a maximum of 50 cases could be opened in one month. A developer had many choices to make this happen in the existing code. However, instead of changing the existing code, they created a new class that would handle only the premium support-related functionalities. This was a good decision because of the single responsibility principle, as discussed in Chapter 1, An Introduction to Apex Design Pattern:

public class AdminPremiumSupport{     
  /** 
   * return SLA in hours 
   */            
   public Integer getSLA() 
   { 
     return 12; 
   }          
  /** 
   * Total allowed support tickets allowed every month is 50 
   */ 
   public Integer allowedTickets()
   { 
     return 50; 
   } 
} 

Now, every time any information regarding the SLA or allowed tickets per month is needed, the following Apex code can be used:

if(Account.supportType__c == 'AdminBasic') 
{ 
  AdminBasicSupport support = new AdminBasicSupport(); 
  System.debug('Support SLA is - '+support.getSLA()); 
                 
}else{ 
  AdminPremiumSupport support = new AdminPremiumSupport(); 
  System.debug('Support SLA is - '+support.getSLA()); 
} 

As we can see in the preceding example, instead of adding some conditions to the existing class, the developer decided to go with a new class. Each class has its own responsibility, and they only need to be changed for one reason. If any change is needed in the basic support, then only one class needs to be changed. As we all know, this design principle is known as the single responsibility principle.

Business was doing exceptionally well in the call center, and they planned to start the gold and platinum support as well. Developers started facing issues with the current approach. Currently, they have two classes for the basic and premium support and requests for two more classes are in the pipeline. There was no guarantee that the support type will not remain the same in future. Because of every new support type, a new class is needed; and therefore, the previous code needs to be updated to instantiate these classes. The following code will be needed to instantiate these classes everywhere:

if(Account.supportType__c == 'AdminBasic') 
{ 
  AdminBasicSupport support = new AdminBasicSupport(); 
  System.debug('Support SLA is - '+support.getSLA()); 
                 
}else if(Account.supportType__c == 'AdminPremier') 
{ 
  AdminPremiumSupport support = new AdminPremiumSupport(); 
  System.debug('Support SLA is - '+support.getSLA()); 
                 
}else if(Account.supportType__c == 'AdminGold') 
{ 
  AdminGoldSupport support = new AdminGoldSupport(); 
  System.debug('Support SLA is - '+support.getSLA()); 
                 
}else{ 
  AdminPlatinumSupport support = new AdminPlatinumSupport(); 
  System.debug('Support SLA is - '+support.getSLA()); 
} 

We are only considering the getSLA() method, but in a real application, there can be other methods and scenarios as well. The preceding code snippet clearly depicts the code duplicity and maintenance nightmare.

The following image shows the overall complexity of the example that we are discussing:

Factory method pattern

Although they are using a separate class for each support type, an introduction to a new support class will lead to changes in the code in all existing code locations where these classes are being used. The development team started brainstorming to make sure that the code is capable of being extended easily in the future with the least impact on the existing code. One of the developers came up with a suggestion to use an interface for all support classes so that every class can have the same methods and they can be referred to using an interface.

The following interface was finalized to reduce the code duplicity:

public Interface IAdminSupport{ 
  Integer getSLA() ; 
  Integer allowedTickets(); 
} 

Note

Methods defined within an interface have no access modifiers and just contain their signatures.

Once an interface was created, it was time to update existing classes. In our case, only one line needed to be changed and the remaining part of the code was the same because both the classes already have the getSLA() and allowedTickets() methods.

Let's take a look at the following line of code:

public class AdminPremiumSupport{ 

This will be changed to the following code:

public class AdminBasicSupportImpl implements IAdminSupport{ 

The following line of code is as follows:

public class AdminPremiumSupport{ 

This will be changed to the following code:

public class AdminPremiumSupportImpl implements IAdminSupport{ 

The AdminGoldSupportImpl and AdminPlatinumSupportImpl classes are written in the same way.

Note

A class diagram is a type of Unified Modeling Language (UML), which describes classes, methods, attributes, and their relationships among other objects in a system. You can read more about class diagrams at  https://en.wikipedia.org/wiki/Class_diagram.

The following image shows a class diagram of the code written by developers using an interface:

Factory method pattern

Now, the code to instantiate different classes of the support type can be rewritten as follows:

IAdminSupport support = null;  
 
if(Account.supportType__c == 'AdminBasic') 
{ 
  support = new AdminBasicSupportImpl();  
                 
}else if(Account.supportType__c == 'AdminPremier') 
{ 
  support = new AdminPremiumSupportImpl();  
                 
}else if(Account.supportType__c == 'AdminGold') 
{ 
  support = new AdminGoldSupportImpl();  
                 
}else{ 
  support = new AdminPlatinumSupportImpl();  
} 
 
System.debug('Support SLA is - '+support.getSLA()); 

Note

There is no switch-case statement in Apex, and that's why multiple if and else statements are written. According to the product team, a new compiler may be released in 2016 with full support. You can vote for this idea at https://success.salesforce.com/ideaView?id=08730000000BrSIAA0.

As we can see, the preceding code is minimized to create a required instance of a concrete class, and then uses an interface to access methods. This concept is known as program to interface and is one of the most often recommended OOP principles suggested to be followed. As interfaces are kinds of contracts, we already know which methods will be implemented by concrete classes, and we can completely rely on the interface to call them, which hides their complex implementation and logic. It has a lot of advantages and a few of them are loose coupling and dependency injection. We have already discussed interfaces and design principles in the previous chapter.

Note

A concrete class is a complete class that can be used to instantiate objects. Any class that is not abstract or an interface can be considered a concrete class.

We still have one problem in the previous approach. The code to instantiate concrete classes is still present at many locations and will still require changes if a new support type is added. If we can delegate the creation of concrete classes to some other class, then our code will be completely independent of the existing code and new support types.

This concept of delegating creation of similar types of classes is known as the factory method pattern.

The following class can be used to create concrete classes and will act as a factory:

/** 
 * This factory class is used to instantiate concrete class  
 * of respective support type 
 * */ 
public class AdminSupportFactory { 
 public static IAdminSupport getInstance(String supporttype){ 

    IAdminSupport support = null;      
    if(supporttype == 'AdminBasic') 
    { 
      support = new AdminBasicSupportImpl();  
             
    }else if(supporttype == 'AdminPremier') 
    { 
      support = new AdminPremiumSupportImpl();  
             
    }else if(supporttype == 'AdminGold') 
    { 
      support = new AdminGoldSupportImpl();  
             
    }else if(supporttype == 'AdminPlatinum') 
    { 
      support = new AdminPlatinumSupportImpl();  
    }  
    return support ; 
  } 
} 

In the preceding code, we only need to call the getInstance(string) method, and this method will take a decision and return the actual implementation. As return type is an interface, we already know the methods that are defined and we can use the method without actually knowing its implementation. This is a very good example of abstraction.

The final class diagram of the factory method pattern that we discussed will look like this:

Factory method pattern

The following code snippet can be used repeatedly by any client code to instantiate a class of any support type:

IAdminSupport support = AdminSupportFactory.getInstance ('AdminBasic');  
System.debug('Support SLA is - '+support.getSLA()); 
Output : Support SLA is - 40 

Reflection in Apex

The problem with the preceding design is that whenever a new support type needs to be added, we need to add a condition to AdminSupportFactory.

We can store the mapping between a support type and its concrete class name in custom setting. This way, whenever a new concrete class is added, we don't even need to change the factory class , only  add a new entry to custom setting.

Consider custom setting created by the Support_Type__c name with the Class_Name__c field name of the text type with the following records:

Name

Class name

AdminBasic

AdminBasicSupportImpl

AdminGolden

AdminGoldSupportImpl

AdminPlatinum

AdminPlatinumSupportImpl

AdminPremier

AdminPremiumSupportImpl

However, using reflection, the AdminSupportFactory class can also be rewritten to instantiate service types at runtime as follows:

/** 
 * This factory class is used to instantiate concrete class  
 * of respective support type 
 * */ 
public class AdminSupportFactory {     
  public static IAdminSupport getInstance(String supporttype) 
  { 
    //Read Custom setting to get actual class name on basis of Support type 
    Support_Type__c supportTypeInfo = Support_Type__c.getValues(supporttype); 
         
    //from custom setting get appropriate class name 
    Type t = Type.forName(supportTypeInfo.Class_Name__c); 

    IAdminSupport retVal = (IAdminSupport)t.newInstance(); 

 
    return retVal;  
  }  
} 

In the preceding code, we are using the Type system class. This is a very powerful class used to instantiate a new class at runtime. It has the following two important methods:

  • forName: This returns a type that is equivalent to a passed string
  • newInstance: This creates a new object for a specified type

Inspecting classes, methods, and variables at runtime without knowing a class name, or instantiating a new object and invoking methods at runtime is known as reflection in computer science.

Note

Apex does not support full reflection and only supports a subset of it. This is a very handy and useful feature supported by many programming languages; you can vote for this idea to get it implemented at https://success.salesforce.com/ideaView?id=08730000000BVaAAK.

One more advantage of using the factory method, custom setting, and reflection together is that if in future one of the support types needs to be replaced by another service type permanently, then we only need to change the appropriate mapping in custom setting without any code change.

..................Content has been hidden....................

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