Application of The Strategy Design Pattern

Ikhiloya Imokhai
4 min readMay 14, 2020
Photo by Hassan Pasha on Unsplash

The Strategy design pattern is a behavioral software design pattern that defines a family of algorithms, encapsulates each one, and makes them interchangeable[1]. It enables the selection of the clients at runtime. The Strategy pattern is flexible since it defers the decision about which algorithm to use until runtime.

To find out more about the Strategy design pattern and the ideal scenarios for use, please see the reference section.

In this post, I’ll explain how I leveraged the strategy design pattern to solve my challenge.

Problem statement

To be able to handle multiple email clients and storage types depending on the environment or profile in which the application is running.
My project requires that Gmail client and local file storage be used for sending mails and storing files respectively while working locally[dev-profile].

Also, I needed to use the SendGrid mail client and AWS storage service for sending emails and storing files respectively while in production[prod-profile].

In summary, here is what I want to achieve:
dev profile: Gmail client and local file storage should be used for sending emails and storing files.
prod profile: SendGrid client and AWS storage for sending mails and storing files.

This implementation is done using the Spring boot framework and leverages heavily on the inversion of control and dependency injection it provides.

We’ll explore these applications independently.

Let’s dive in…..

  1. Email Implementation.

The diagram below shows the high-level design of the Strategy pattern for our email service.

UML diagram for Mail Strategy

The strategy pattern ensures that the appropriate email client is used to send emails depending on your active profile: dev or prod.
The fundamental concept of the strategy pattern is to aggregate a family of related entities such that the application knows the appropriate client to use at runtime. This eliminates the need to write boilerplate code to ensure that the right client is used.

Let’s define the various classes as shown in the diagram above

  1. MailStrategy

This is an interface that contains the various method definition for an email client. It defines the contract for all other concrete implementation. An abstract class could also suffice.

2. SendGridEmailStrategy

This is a concrete implementation of the MailStrategy contract. It holds the logic for sending emails using the SendGrid client.

3. GmailStrategy

This is a concrete implementation of the MailStrategy contract. It holds the logic for sending emails using the Gmail client.

4. MailFactory

To ensure that the appropriate email client is used at runtime depending on the specified active profile, a factory is used. This leverages the factory creation design pattern to create the right mail client.

5. MailService

A typical service class that holds the logic for sending emails. It is in the service layer that the beauty of the strategy pattern is seen. It does not need to worry about the specific implementation of the different mail strategies rather it just programs to the MailStrategy interface and lets the appropriate implementation be called at runtime.

Remember the basic OOP principle:

“program to interface, not implementations”

2. File Storage Implementation.

The diagram below shows the high-level design of the storage strategy pattern implementation.

UML diagram for Storage Strategy

Similarly, let’s define the various classes as shown in the diagram above.

  1. StorageStrategy

This interface defines the contract for file storage. All other concrete implementations must abide by this contract. It contains just a method to upload a file as can be seen below:

2. AwsStorageStrategy

This provides the amazon web service(AWS) specific concrete implementation of file upload. It encapsulates the logic for uploading files to the AWS storage as can be seen below. This implementation is called only when the prod profile is specified during the build.

3. FileStorageStrategy

This is called when the dev profile specified. It holds the logic for storing files using the local file system as can be seen below. It stores files to a specified folder locally.

4. StorageFactory

This performs a similar function as the MailFactory already explained.

5. StorageService

This is analogous to the MailService explained above. It is the service layer that holds the logic to upload files irrespective of the different implementations.

Conclusion

As can be seen from the above implementations, it is very easy to switch between the dev and prod profiles without having to worry about the necessary implementation as it is taken care of at runtime owing to the strategy design pattern.

You could easily switch profile in spring by running this command:

Viola all the implementation code remains intact.

You can find the source code in the repository below.

Thanks for reading and your suggestions and comments are welcomed.

References

  1. Head First Design Patterns

📝 Save this story in Journal.

👩‍💻 Wake up every Sunday morning to the week’s most noteworthy stories in Tech waiting in your inbox. Read the Noteworthy in Tech newsletter.

--

--