Friday, October 4, 2019

Strategy Design Pattern

Greetings!

Strategy pattern is a behavioral pattern. It is one of the easiest patterns to learn and one of the most used pattern.

Source code for this blog post can be found in my github account. [source-code]

Let's see the GoF definition;
"Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it."


"Algorithm" in this definition is not necessarily a mathematical algorithm. It is something that do a thing. Basically algorithm is just another name for a strategy.
This leads us to;

  • A class should be configured with an algorithm instead of implementing an algorithm directly.
  • An algorithm should be selected an exchanged at run time.

When we start OOP, we are fond of inheritance. But when it comes to separating the code into more manageable way, we choose composition over inheritance.
"Consider 'has-a' instead of 'is-a'"

We achieve this by extracting the volatile parts of our code and encapsulate them as objects.
"Separate the parts of the code that will change the most."

Working Example

Let's take a practical example. We need to have file storage service which can handle save, retrieve operations. There, we can have a class and simply put the saving logic into that class. May be, from the begining it is not clear. Let's say we choose to save files in local storage. But what if we want to save files in AWS? It is clear that we should manage it in separate class. We can start with local file storage and easily swith to AWS.

We start by creating our FileService

package com.slmanju.patterns;

public interface FileService {

    void save();

    void retrieve();

}


Let's create the LocalFileService

package com.slmanju.patterns;

public class LocalFileService implements FileService {

    @Override
    public void save() {
        System.out.println("Save file in local disk.");
    }

    @Override
    public void retrieve() {
        System.out.println("Retrieving file from local disk.");
    }

}


We are going to use this in our StorgeService

package com.slmanju.patterns;

public class StorageService {

    private FileService fileService;

    public StorageService(FileService fileService) {
        this.fileService = fileService;
    }

    public void setFileService(FileService fileService) {
        this.fileService = fileService;
    }
    
    public void save() {
        fileService.save();
    }

    public void retrieve() {
        fileService.retrieve();
    }

}


All set. Let's run our application.

package com.slmanju.patterns;

public class App {

    public static void main(String[] args) {
        FileService localFileService = new LocalFileService();

        StorageService storageService = new StorageService(localFileService);
        storageService.save();
        storageService.retrieve();

        System.out.println("------");

        FileService awsFileService = new AwsFileService();
        storageService.setFileService(awsFileService);
        storageService.save();
        storageService.retrieve();
    }

}



Remember;
  • Encapsulate what varies.
  • Favor composition over inheritance.
  • Program to interface, not implementation.

Happy coding!


6 comments: