Design Patterns in Go: Strategy

Introduction

The strategy pattern is a behavorial design pattern that allows you to define a family of algorithms, encapsulate them as an object, and with the help of interfaces make them interchangeable.

What does it look like?

A short explanation:

  1. The context wants to apply some sort of algorithm, and for that purpose it contains an object which implements the Strategy interface.
  2. When the Strategy is needed an object of either StrategyA or StrategyB is initiated, and the algorithm method is called.

It could be argued that this is a form of dependency injection since methods are only called on an interface and not on concrete objects.

Implementation in Go

First open your terminal in the directory where you want to put the code, and type:

go mod init github.com/designpatterns_strategy

Then open your favorite IDE, and create a main.go file. Start this file by adding:

package main

import "fmt"

For this we will imagine to work for a travel hub, where customers can come and we can either provide them with train travel or rental car, depending on which is the best way to reach their destination.

First we define a very simple TravelStrategy:

type TravelStrategy interface {
	Travel(distance int16)
}

In this very simple implementation all our customers can do is travel.

So, let us implement the TrainStrategy:

type TrainStrategy struct{}

func (ts *TrainStrategy) Travel(distance int16) {
	fmt.Printf("Travelled %d kilometers by train", distance)
}

TrainStrategy in this case is an empty struct. All the Travel method does is print out the distance travelled.

The CarStrategy is similar:

type CarStrategy struct{}

func (cs *CarStrategy) Travel(distance int16) {
	fmt.Printf("Travelled %d kilometers by car", distance)
}

Now define the TravelHub:

type TravelHub struct {
	strategy TravelStrategy
}

func NewTravelHub(strategy TravelStrategy) *TravelHub {
	return &TravelHub{
		strategy: strategy,
	}
}

func (hub *TravelHub) CustomerTravel(distance int16) {
	hub.strategy.Travel(distance)
}

Some notes:

  1. The TravelHub has one field, a type which implements the TravelStrategy interface.
  2. NewTravelHub is a kind of constructor. Its only parameter is a type which implements the TravelStrategy interface.
  3. We need to send our customers on our way in the CustomerTravel method. In this method we call Travel on the strategy field.

Now it is time for the test:

func main() {
	travelhub := NewTravelHub(&TrainStrategy{})
	travelhub.CustomerTravel(20)
}

A line by line explanation:

  1. First instatiate a TravelHub object and pass it a TrainStrategy object.
  2. Send the customers on their way

Conclusion

As you can see, it is pretty easy to implement this pattern in Go. Yet, due to its use of interfaces, it is quite flexible.

What I notice is, but I have not used this pattern much, is that we use some kind of constructor injection which seems to be the best suited for this pattern.

Leave a Reply

Your email address will not be published. Required fields are marked *