📗 Object Behavioural: Strategy
Tags: designpatterns
Published: 2021-07-10 09:28:13

## TL;DR;
Use this pattern if you have multiple similar classes, which differ in some logic implementation; your code contains if-else's to determine a proper algorithm to use.
_Second pattern in a row targets to eliminate if-else's where possible - nice._

## Definition
Strategy pattern allows separating algorithms from the clients using them.

Here is a formal diagram:
![UML diagram of Strategy pattern](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/20czcbuk75kqpskb5dw5.png)

We have an object of type _Context_, which holds a reference to an _IStrategy_ object. _Context_ passes some work to this _IStrategy_ object.

Sounds pretty much the same as [State Pattern](), but the strategy is set once at the _Context_ instantiating time (while _state_ is changing after _Context_ is instantiated).

## Implementation
Let's write some code.
We still have a _Player_, but this time we're more interested in its codecs.
So we would end up with something like this:
```typescript
export default class Player {
  private paramA: number;
  private paramB: number;
  private paramC: number;

  constructor(private codecType: AlgorithmType, private stream: Stream) {
    // Setting up params
  }

  play() {
    const algorithm = this.getAlgorithm();

    switch (algorithm) {
      case 'A':
        this.decodeA();
        break;
      case 'B':
        this.decodeB();
        break;
      case 'C':
        this.decodeC();
        break;
    }

    console.log('>>>> Play');
  }

  private getAlgorithm(): AlgorithmType {
    // Some logic happens here to determine appropriate algorithm.
    return this.codecType;
  }

  private decodeA(): Stream {
    // Actual algorithm to decode the stream in some way
    console.log('>>>> Decoding with A', this.paramA);
    return this.stream;
  }

  private decodeB(): Stream {
    // Actual algorithm to decode the stream in some way
    console.log('>>>> Decoding with B', this.paramB);
    return this.stream;
  }

  private decodeC(): Stream {
    // Actual algorithm to decode the stream in some way
    console.log('>>>> Decoding with C', this.paramC);
    return this.stream;
  }
}
```
We have all different ways of decoding a stream inside the _Player_ class. We have a switch statement to determine which particular algorithm we should use. Not that bad right now, but imagine a real life situation where _decode_ functions could contain tens of lines of code.

What _Strategy_ suggests is:
* create an interface to describe the strategy class 
* move each decoding algorithm into a standalone strategy class
* add a reference to the particular instance to you _Player_

Well, let's do all these with one small change: let's not create a class for each algorithm. Look at this:
```typescript
interface Strategy {
  decode(stream: Stream, context: DecodeContext): Stream;
}

class StrategyA implements Strategy {
  decode(stream: Stream, context: DecodeContext) {
    // implementation
  }
}
```
We're in the TypeScript (JavaScript) world, functions are the first class citizens, we can pass them everywhere, we don't need to create a class just to have one function in it.

So this is what our pattern implementation might look like:
```typescript
export default class Player {
  private paramA: number;
  private paramB: number;
  private paramC: number;

  constructor(private decode: Decode, private stream: Stream) {
    // Setting up params
  }

  play() {
    this.decode(this.stream, {
      a: this.paramA,
      b: this.paramB,
      c: this.paramC,
    });
    console.log('>>>> Play');
  }
}
```

And all decoding algorithms now live in their own functions:
```typescript
export const decodeA: Decode = (stream: Stream, { a }: DecodeContext) => {
  // Actual algorithm to decode the stream in some way
  console.log('>>>> Decoding with A', a);
  return stream;
};

export const decodeB: Decode = (stream: Stream, { b }: DecodeContext) => {
  // Actual algorithm to decode the stream in some way
  console.log('>>>> Decoding with B', b);
  return stream;
};

export const decodeC: Decode = (stream: Stream, { c }: DecodeContext) => {
  // Actual algorithm to decode the stream in some way
  console.log('>>>> Decoding with C', c);
  return stream;
};
```

## What will it cost?
*Potential Cons:*
* We might increase the number of files, which some might consider a bad thing
* As we define the _Decode_ interface for every algorithm we're passing all the params each algorithm might need. Less then ideal

*Potential Pros:*
* We got rid of the _switch_ statement
* We separated concerns by moving algorithms implementations outside the _Player_ class

## Conclusion
I do like separating concerns and avoiding conditionals when possible, so this pattern seems really useful to me so far.

Hope this was a bit helpful 🙃
[Source code if needed](https://github.com/Jokerwolf/design-patterns-study)