DEV Community

Jean Vidal
Jean Vidal

Posted on • Edited on

From Theory to Practice: Strategy Pattern in Real-world Scenarios

Introduction

Some time ago I was with a task to implement the following flow:

  1. Get data from database;
  2. Filter data by some criteria;
  3. For each register, access one API to get data and complete informations;
  4. Record all data on database.

At this time, everything was ok. But...

The Problem

I had not received any API and there was a risk of not receiving it and having to do web scraping.

So I needed to implement something where even if the data source changed there wouldn't be a big impact.

The Options

At least, I knew the data could be obtained by:

  1. API
  2. Web Scraping

So I needed to implement something that could easily choose the data source.

The Solution

After searching for while I found the perfect pattern for my problem.

Strategy Pattern

This pattern allows us to encapsulate different strategies and the possibility to choose one at runtime based on some conditions.

In my case I defined separate strategies for get data by API or Web Scraping and dynamically selected the better strategy based on my needs.

See how it works:

1- I created an interface with the method signature that will retrieve the data;

public interface DataRetrievalStrategy {
    List<DataItem> retrieveData(String dataStringOrObjectData);
}
Enter fullscreen mode Exit fullscreen mode

2- I created the classes that will implement my interface;

public class APIDataRetrievalStrategy implements DataRetrievalStrategy {
    public List<DataItem> retrieveData(String dataStringOrObjectData) {
        // ...
        return dataItems;
    }
}

public class WebScrapingDataRetrievalStrategy implements DataRetrievalStrategy {
    public List<DataItem> retrieveData(String dataStringOrObjectData) {
        // ...
        return dataItems;
    }
}
Enter fullscreen mode Exit fullscreen mode

3- I implemented all the strategies that I would try.

  • WebScrapingSeleniumDataRetrievalStrategy;
  • WebScrapingPuppeteerDataRetrievalStrategy;
  • WebScrapingPythonDataRetrievalStrategy;
  • WebScrapingRDataRetrievalStrategy.

4- So I just needed to instantiate what I would use.

public class Microservice
{
    public static void Main(string[] args)
    {
        DataService dataService = new DataService();

        dataService.SetStrategy(new WebScrapingSeleniumDataRetrievalStrategy());
        List<DataItem> seleniumDataItems = dataService.RetrieveData("dataStringOrObjectData");

        dataService.SetStrategy(new WebScrapingPuppeteerDataRetrievalStrategy());
        List<DataItem> puppeteerDataItems = dataService.RetrieveData("dataStringOrObjectData");

        dataService.SetStrategy(new WebScrapingPythonDataRetrievalStrategy());
        List<DataItem> pythonDataItems = dataService.RetrieveData("dataStringOrObjectData");

        dataService.SetStrategy(new WebScrapingRDataRetrievalStrategy());
        List<DataItem> rDataItems = dataService.RetrieveData("dataStringOrObjectData");
    }
}
Enter fullscreen mode Exit fullscreen mode

Conclusion

In the end, I was able to solve my problem by providing options for the team to make decisions. I also left the option of implementing some ways of measuring each strategy's performance, to have a more efficient solution in terms of performance, but this was not possible due to the time available to act.

It is worth mentioning that a design pattern solves a specific problem and in this case, this pattern solved mine perfectly. It will not always be possible/necessary to use design patterns and it is extremely important that we, software engineers, are able to evaluate possible paths.

We're done here folks.

I hope you all enjoyed it and that this helps in some way.

Top comments (0)