From Theory to Practice: Strategy Pattern in Real-world Scenarios
Tags: strategypattern, designpatterns, dotnet, programming
Published: 2024-04-22 16:37:14

## 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;
```c#
public interface DataRetrievalStrategy {
    List<DataItem> retrieveData(String dataStringOrObjectData);
}
```
2- I created the classes that will implement my interface;
```c#
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;
    }
}
```
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.
```c#
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");
    }
}
```

## 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.