Strategy Design Pattern in Apex

Strategy Design Pattern in Apex

Strategy Pattern allows you to make a game-time call. The strategy pattern is a behavioral pattern that helps you control dynamic application flow. Strategy Design Pattern in Apex allows us to define a family of algorithms through multiple classes which implement an interface. The choice of which implementation to use is determined at runtime.

What is the Strategy Design Pattern in Apex?

The strategy pattern ensures that different solutions are available for the same problem. It chooses the appropriate solution based on the user input at the runtime. This design pattern is also called the policy pattern. When do we want to use the strategy pattern? We want to use the strategy pattern whenever we have a deeply nested switch or conditional if-else statement within our application’s core logic.

Here’s an example of a switch statement and it’s pretty easy to read because it’s so simple in its nature. What we’re doing is we’re taking the record types developer name. When it’s retail we’ll handle the retail request. When it’s wholesale we’ll handle the wholesale request.

 @AuraEnabled(cacheable=true)
 public static FactResponse getAnimalFacts(Id recordId) {

   FactResponse result;

   String recordTypeDeveloperName = selector.getAccountFromId(recordId).RecordType.DeveloperName;

   switch on recordTypeDeveloperName {
     when 'Retail' {
       result = handleRetailRequest();
     }
     when 'Wholesale' {
       result = handleWholesaleRequest();
     }
     // ...
   }
   return result;
 }

But the problem is that as your application grows and matures this different context that you’re going to need to be executing in might become more and more complex. You’re going to need to keep writing these multiple separate handlers for every different context. You find yourself and each one of these handlers is just like another private method within this one particular class so the responsibility of the class actually grows and grows and grows as you handle all of these different contexts.

So in order to help our code base stay easier to maintain and easier to read in the long run we’re going to try to introduce the strategy design pattern here.

Class Diagram for the Strategy pattern

Here is a class diagram for the Strategy pattern. Few key concepts, we’ll always start with some context and that context depends upon the interface. We call this interface the strategy interface. Here we’re just saying this interface has a void method called an algorithm.

Class Diagram for the Strategy pattern

Then we’ll have multiple strategies concrete implementations or concrete strategies that implement the strategy interface. The context will choose the strategy that it needs at runtime and call it’s about the implementation of the algorithm.

Implementation of Strategy pattern in Apex

Let’s see how we can implement the Strategy pattern in Apex.

public class AnimalService {

 private static final Map<String, IAnimalService> RECORD_TYPE_TO_SERVICE = new Map<String, IAnimalService>{
   'Retail' => new DogService(),
   'Wholesale' => new CatService()
 };

 @TestVisible
 private static AnimalService.Selector selector = new AnimalService.Selector();

 @AuraEnabled(cacheable=true)
 public static FactResponse getAnimalFacts(Id recordId) {

   String context = selector.getAccountFromId(recordId)
     .RecordType.DeveloperName;

   IAnimalService strategy = RECORD_TYPE_TO_SERVICE.get(context);

   return strategy.getFacts();

 }

}

Here is an example of an Interface class.

public interface IAnimalService {
   FactResponse getFacts();
 }

 public class FactResponse {
   @AuraEnabled public String factAbout;
   @AuraEnabled public String fact;
 }

 @TestVisible
 private with sharing class Selector {

   public virtual Account getAccountFromId(Id recordId) {
     return [ SELECT Id, RecordType.DeveloperName FROM Account WHERE Id = :recordId];
   }

 }
}

So keep in mind here that what we care about is this interface right we’re relying on the interface to tell us how we’re going to get a fact response and we don’t really care if we’re going to get dog responses or if we’re going to get cat responses. All that we care about is hey you’re just going to tell us to get facts and we will get you facts and a factor response back.

Cat Service

public class CatService implements AnimalService.IAnimalService {

 public AnimalService.FactResponse getFacts() {
   AnimalService.FactResponse result = new AnimalService.FactResponse();
   Http http = new Http();
   HttpRequest request = new HttpRequest();
   request.setEndpoint('https://catfact.ninja/fact');
   request.setMethod('GET');
   HttpResponse response = http.send(request);
   if (response.getStatusCode() == 200) {
     result.factAbout = 'cats';
     CatService.StructuredResponse catFact = (CatService.StructuredResponse) (JSON.deserialize(
       response.getBody(),
       CatService.StructuredResponse.class
     ));
     result.fact = catFact.fact;
   }
   return result;
 }

}

Response class

 public class StructuredResponse {
   @AuraEnabled
   public String fact;
   @AuraEnabled
   public Integer length;
 }

Dog Service

public class DogService implements AnimalService.IAnimalService {

 public AnimalService.FactResponse getFacts() {
   AnimalService.FactResponse result = new AnimalService.FactResponse();
   Http http = new Http();
   HttpRequest request = new HttpRequest();
   request.setEndpoint('https://dog-facts-api.herokuapp.com/api/v1/resources/dogs?number=1');
   request.setMethod('GET');
   HttpResponse response = http.send(request);
   if (response.getStatusCode() == 200) {
     result.factAbout = 'dogs';
     List<DogService.StructuredResponse> facts = (List<DogService.StructuredResponse>) (JSON.deserialize(
       response.getBody(),
       List<DogService.StructuredResponse>.class
     ));

     result.fact = facts[Integer.valueOf((Math.random() * facts.size()))].fact;
   }
   return result;
 }
}

Dog response class

 public class StructuredResponse {
   @AuraEnabled
   public String fact;
 }

Dynamic Mapping

Now here is an example of how we can convert our first class into a Strategy Design Pattern.

public class AnimalService {

 private static final Map<String, IAnimalService> RECORD_TYPE_TO_SERVICE = new Map<String, IAnimalService>{
   'Retail' => new DogService(),
   'Wholesale' => new CatService()
 };
 ...
}

Use Custom Metadata

Use custom metadata to make the above class dynamic with custom metadata.

private Map<String, IAnimalService> getContextMapFromMetadata() {

 Map<String, IAnimalService> result;
 for (
   RecordType_To_Service__mdt recordTypeToServiceMapping : RecordType_To_Service__mdt.getAll()
     .values()
 ) {
   String key = recordTypeToServiceMapping.RecordType_DeveloperName__c;
   IAnimalService value = (IAnimalService) (Type.forName(
       recordTypeToServiceMapping.Apex_Class_Name__c
     )
     .newInstance()); 
   result.put(key, value);
 }
 return result;
}

Strategy Design Pattern in Apex Video

Check out our recording for a line-by-line explanation of the code.

Check our other apex design pattern in our blog post.

When should we use Strategy Design Pattern?

Strategy pattern should be used when you need to choose between one of many algorithms to solve the problem, and you won’t know which one to use until runtime.

Strategy pattern helps to break out custom implementations from the main code path while minimizing the use of large switch statements.

Summary

Strategy Design Pattern is used to define a family of algorithms, encapsulating each one and making them interchangeable and selectable at runtime. Check recording for real-time examples using LWC in Apex.

Amit Chaudhary

Amit Chaudhary

Amit Chaudhary is Salesforce Application & System Architect and working on Salesforce Platform since 2010. He is Salesforce MVP since 2017 and have 17 Salesforce Certificates. He is a active blogger and founder of Apex Hours.

Share this article

Leave a reply

Subscribe for Email

Enter your email address to subscribe to this blog and receive notifications of new posts by email.

Join 3,541 other subscribers

Our Supporter

RECENT POSTS

Apex Hours

Apex Hours is one stop platform to learn Salesforce skills and technology

Join our Newsletter and get tips and tricks how to explore the salesforce for free!