Tuesday, June 16, 2015

Implementing Strategy Design Pattern in Apex


This post is part of the series - Design Pattern in Apex

The strategy design pattern is the one which allows an algorithm's behavior to be chosen at run time. It is used to define a set of algorithms to solve a common problem. It allows unique logic per algorithm via encapsulations, but ensures that all algorithms are interchangeable at run time. Here the abstraction is achieved by an interface, whereas individual implementations are done at derived classes. So basically it looks like -
Where I should use this design pattern?
This design pattern should be used when you are planning to perform an operation that has a common end goal, but there can be different approaches/ways to achieve that goal. All these approaches can be chosen by client at run-time.

Let me tell you few real-life Salesforce based example where this design pattern can be used -
  • Let's say in the account detail page, you want to display the current temperature of the city where the office located. Now based on the country/state, you may need to collect the temperature information from different websites, but your final goal is always the same i.e. showing the temperature.
  • Let's say you are calculating discounts per product based on month. Every month, the discount amount is getting calculated by different complex algorithms/formulas. Here also, your final goal is same i.e. calculating the discounts.
  • Let's say you want to set contact's preferences based on his/her geography. There can be one algorithm which takes care of all Apac contacts, whereas another one takes care of Europe contacts, etc. So here also you have different algorithms to select the preferences, but at the end, you final goal is same i.e. setting preferences.
Now let's come to the point -
"Open-closed principle" is one of the important strategy of object oriented design. As the below picture tells, in this strategy, developer encapsulate interface details in base class, and bury implementation details in derived classes. Client can couple themselves to an interface. As a result client will never experience any impact due to changes : no impact when the number of derived classes changes, or no impact when implementation of derived class changes.



Components of Strategy Design Pattern -
  • Client - This is the entry point of the strategy pattern.
  • Context - This is the place where decision will be taken at run-time to identify the strategy.
  • Interface Strategy - An interface, which defines a set of methods that will be implemented by the concrete strategy classes.
  • Concrete Strategy - A group of classes that implement the methods defined by the strategy Interface. These classes encapsulate any logic that is unique to that particular concrete strategy.
UML -



Implementation -

Problem statement -
Design a solution to retrieve the current temperature for an account. Based on the geography, temperature information will be collected from different freely available Weather APIs. The design must be expandable for future Weather APIs.

UML -

Description -
  • Weather.cls - Context
  • WeatherService.cls - Interface Strategy
  • WeatherForcastType1 - Concrete Strategy
  • WeatherForcastType2 - Concrete Strategy
Code -

WeatherService.cls - Interface Strategy -
public interface WeatherService {
    Double getTemperatureInCelsius();
    Double getTemperatureInFarhenheit();
}
Now we need to implement the above interface to create concrete classes/strategies.
Below are the concrete strategies -
WeatherForcastType1 - Concrete Strategy
public class WeatherForcastType1 implements WeatherService {
    public Double getTemperatureInCelsius() {
        //Fetch Temperature from Weather API# 1
        return temperature; 
    }
    
    public Double getTemperatureInFarhenheit() {
         //Fetch Temperature from Weather API# 1
         return temperature; 
    }
}

WeatherForcastType2 - Concrete Strategy
public class WeatherForcastType2 implements WeatherService {
    public Double getTemperatureInCelsius() {
        //Fetch Temperature from Weather API# 2
        return temperature; 
    }
    
    public Double getTemperatureInFarhenheit() {
         //Fetch Temperature from Weather API# 2
         return temperature; 
    }
}

Now it's turn for the context class -
Weather.cls -
public class Weather {
    private WeatherService weatherServiceType;
 
    private Set<String> forecastType1Countries = new Set<String>{'United Kingdom','Europe', 'North America', 'South America', 'Switzerland'};
    private Set<String> forecastType2Countries = new Set<String>{'India','Antartica', 'Australia', 'Asia'};
 
    public Weather(String continent) {
 
        if(forecastType1Countries.contains(continent)) {
            weatherServiceType = new WeatherForcastType1();
        } else if (forecastType2Countries.contains(continent)) {
            weatherServiceType = new WeatherForcastType2();
        } 
    }
 
    public Double getTemperatureInCelsius() {
        return weatherServiceType.getTemperatureInCelsius(weatherResponse);
    }
    
    public Double getTemperatureInFarhenheit() {
        return weatherServiceType.getTemperatureInFarhenheit(weatherResponse);
    }
}

Finally, strategies can be executed by client like -
Weather weather = new Weather('India');
Double tempCel = weather.getTemperatureInCelsius();
Double tempFar = weather.getTemperatureInFarhenheit();
or
Weather weather = new Weather('Switzerland');
Double tempCel = weather.getTemperatureInCelsius();
Double tempFar = weather.getTemperatureInFarhenheit();

As you can see in the above example, we can use different weather APIs for WeatherForcastType1  and WeatherForcastType2. But which API is getting used is completely unknown to the client. At the same time, at any point, we can modify any of the concrete classes to change the Weather API, but client will not never notice any impact.

That's the beauty of Strategy Design Pattern. If you have any feedback/comment, please let me know. Thanks.

0 comments:

Post a Comment