Design Patterns in Apex: Strategy Pattern

To kick start the series of Design Patterns in Apex I’m going to cover the Strategy Pattern, as it is one of the most common patterns – and one that can be applied well in Apex/Salesforce.

1. What does it do?

The strategy pattern allows an algorithm’s behaviour to be chosen at runtime. It can be used to define a family of algorithms that solve a common goal. It permits unique logic per algorithm via encapsulation, but ensures that all algorithms are interchangeable at runtime. The abstraction is captured by an interface, whilst individual implementations are buried in derived classes.

2. When should it be used?

The strategy pattern should be used when you wish to perform an operation that has a common end-goal, but has several different approaches to get there – which can be chosen by the client at runtime.

Some real-world Salesforce based examples:

  • You wish to retrieve the local weather for an Account based on its location, but need to use several different Weather APIs to get the best results – based on the country which the Account is located in.
  • You have a complex algorithm to calculate expected revenue on Opportunities, and this algorithm gets refined each month. Each month should still be calculated with its respective algorithm You wish to build something that is both expandable, and allows you to retrospectively perform calculations.

3. How do I use it?


The strategy pattern has several components.

  • Client – The entry point for the strategy pattern
  • Context – Used to control which strategy is chosen
  • Interface Class – An interface, which is the strategy itself.  It defines a set of methods that will be implemented by the concrete strategy classes.
  • Concrete Strategies – A group of classes that implement the methods defined by the strategy interface. They encapsulate any logic that is unique to that particular concrete strategy.

Generic UML

Generic Strategy Structure

4. Example Implementation

The scenario

Design a solution to retrieve the current weather for an Account in Salesforce. There are several freely available Weather APIs, however, some perform better than others in certain continents. The code must be expandable for future Weather APIs.

Weather Service UML

Strategy Pattern

Description of classes

Class Name Role
Weather.cls Context
WeatherService.cls Interface – Strategy
WeatherForcastio.cls Class – Concrete Strategy
WeatherOpenMap.cls Class – Concrete Strategy

Code Walkthrough

First define an interface, which lists the common methods which the concrete strategy classes will implement.

What is an interface?
An interface is an abstract type that is used to specify a list of methods which its non-abstract classes must implement. It adds a layer of formalisation to polymorphism – it enforces that all implementations of a class have a set of predefined behaviours. It can be considered a promise. A promise that all classes which implement it will always contain the methods defined in the interface.

This example will require two methods:

  • getWeather() – which takes latitude and longitude as arguments, and will return the raw JSON response from the Weather API(s)
  • getTemperatureInCelsius() – which takes the JSON response as an argument and will parse out the current temperature, and return it in celsius.
public interface WeatherService {

	String getWeather(String lat, String lon);
	Double getTemperatureInCelsius(String jsonResponse);


Next, the interface must be implemented by the concrete classes, which are deemed as the individual strategies.

In this example, there are two implementations – one which uses and another which uses OpenWeatherMap. Each implementation shares a set of common methods, as defined by the interface, however, it can be seen that the implementations differ.

For example, one API returns the temperature in Fahrenheit, whilst the other returns the temperature in Kelvin. The getTemperatureInCelsius() method implies that the temperature should be returned in Celsius, so each implementation performs the necessary conversion.

public class WeatherForcastio implements WeatherService {

	private final String API_KEY 	= 'xxxxx';

	public String getWeather(String lat, String lon) {

		Http http = new Http();
		HttpRequest req = new HttpRequest();
		HttpResponse res = new HttpResponse();

		Datetime dt =;
		String formattedDateTimeString = dt.format('yyyy-MM-dd\'T\'hh:mm:ss\'Z\'', 'GMT');

						+ '/'+lat+','+lon+','+formattedDateTimeString);

		res = http.send(req);

		return res.getBody();


	public Double getTemperatureInCelsius(String jsonResponse) {

		WeatherForcastioWrapper responseWrapper = WeatherForcastioWrapper.parse(jsonResponse);

		//convert from Fahrenheit to Celsius
		Double temperature = (responseWrapper.currently.temperature - 32) / 1.8000;

		// round to 2 decimal places
		temperature = Math.round(temperature * 100);
		temperature = temperature / 100;

		return temperature;	



public class WeatherOpenMap implements WeatherService {

	public String getWeather(String lat, String lon) {

		Http http = new Http();
		HttpRequest req = new HttpRequest();
		HttpResponse res = new HttpResponse();

		res = http.send(req);

		return res.getBody();


	public Double getTemperatureInCelsius(String jsonResponse) {

		WeatherOpenMapWrapper responseWrapper = WeatherOpenMapWrapper.parse(jsonResponse);

		// convert from Kelvin to Celsius
		Double temperature = responseWrapper.main.temp - 273.15;

		// round to 2 decimal places
		temperature = Math.round(temperature * 100);
		temperature = temperature / 100;

		return temperature;	


Next, the context class. This class contains the logic which defines which strategy is used at runtime.

As the strategies both implement the abstract-type interface, we can use polymorphism to bind the chosen strategy to an instance variable that has the type of the interface – in this case it’s a variable called weatherServiceType which has a type of WeatherService.

As this is a simple example, the logic to choose which strategy is defined in a constructor for the class which takes a continent(string) argument. If the logic was more complex, it could be offloaded to another method which takes more arguments. The context class also has methods which call the appropriate concrete strategy methods.

public class Weather {

	private WeatherService weatherServiceType;

	private Set<String> forcastioCountries = new Set<String>{'Europe', 'North America', 'South America', 'Africa'};
	private Set<String> openMapCountries = new Set<String>{'Antartica', 'Australia', 'Asia'};

	private String weatherResponse;

	public Weather(String continent) {

		if(forcastioCountries.contains(continent)) {
			weatherServiceType = new WeatherForcastio();
		} else if (openMapCountries.contains(continent)) {
			weatherServiceType = new WeatherOpenMap();
		} else {
			throw new WeatherException('Unrecognised continent');

 	public String getWeather(String lat, String lon){
 		weatherResponse = weatherServiceType.getWeather(lat, lon);	
 		return weatherResponse;

 	public Double getTemperatureInCelsius() {
 		return weatherServiceType.getTemperatureInCelsius(weatherResponse);

 	public class WeatherException extends Exception {}


Finally, the strategy can be executed by the client in the following way:

Weather weather = new Weather('Australia');
weather.getWeather('34', '151');
Double temperature = weather.getTemperatureInCelsius();


Weather weather = new Weather('Europe');
weather.getWeather('48', '2');
Double temperature = weather.getTemperatureInCelsius();

Even though behind the scenes, each example is using an entirely different weather API, this complexity is hidden from the client.


2 thoughts on “Design Patterns in Apex: Strategy Pattern

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s