Strategy pattern is used to decide between choosing an algorithm from a bunch of algorithms (that serve the same purpose). This pattern encapsulates the complexity and makes is easier for the client to choose and use an algorithm.
This article demonstrates Strategy pattern implementations in Go. Check the following examples.
Implementation
Here is a simple Implementation of the Strategy pattern-
// Strategy pattern implementation in GoLang
package main
import "fmt"
// Strategy interface
type IStrategy interface {
operation1()
operation2()
}
// First concrete strategy
type ConcreteStrategyOne struct {
}
func NewConcreteStrategyOne() (strategy *ConcreteStrategyOne) {
strategy = &ConcreteStrategyOne{}
return
}
func (strategy *ConcreteStrategyOne) operation1() {
fmt.Println("Strategy One: operation 1")
}
func (strategy *ConcreteStrategyOne) operation2() {
fmt.Println("Strategy One: operation 2")
}
// Second concrete strategy
type ConcreteStrategyTwo struct {
}
func NewConcreteStrategyTwo() (strategy *ConcreteStrategyOne) {
strategy = &ConcreteStrategyOne{}
return
}
func (strategy *ConcreteStrategyTwo) operation1() {
fmt.Println("Strategy Two: operation 1")
}
func (strategy *ConcreteStrategyTwo) operation2() {
fmt.Println("Strategy Two: operation 2")
}
// Strategy class
type Strategy struct {
strategy IStrategy
}
func NewStrategy(strategy IStrategy) (newStrategy *Strategy) {
newStrategy = &Strategy{}
newStrategy.strategy = strategy
return
}
func (strategy *Strategy) someOperation1() {
// perform some operation as per requirement (if required)
strategy.strategy.operation1()
// perform some operation as per requirement (if required)
}
func (strategy *Strategy) someOperation2() {
// perform some operation as per requirement (if required)
strategy.strategy.operation2()
// perform some operation as per requirement (if required)
}
func (strategy *Strategy) someComplexOperation() {
// perform some operation as per requirement (if required)
strategy.strategy.operation2()
// perform some operation as per requirement (if required)
strategy.strategy.operation1()
// perform some operation as per requirement (if required)
}
// Demo
func main() {
fmt.Println("Using Strategy One")
strategy := NewStrategy(NewConcreteStrategyOne())
strategy.someOperation1()
fmt.Println("Using Strategy Two")
strategy = NewStrategy(NewConcreteStrategyTwo())
strategy.someOperation1()
strategy.someOperation2()
strategy.someComplexOperation()
}
Output will be as below-
Using Strategy One
Strategy One: operation 1
Using Strategy Two
Strategy One: operation 1
Strategy One: operation 2
Strategy One: operation 2
Strategy One: operation 1
Examples
Here are a few examples of the Strategy pattern implementation-
Example #1: Transport
Let’s take the example of a transport system.
Transport Interface
// transport.go
package main
type Transport interface {
GetInfo()
Operate()
Repair()
Start()
Stop()
}
Bike Struct
// bike.go
package main
import "fmt"
type Bike struct {
}
func NewBike() (bike *Bike) {
bike = &Bike{}
return
}
func (bike *Bike) GetInfo() {
fmt.Println("Transport type: Bike")
fmt.Println("Number of wheels: 2")
fmt.Println("Average Weight: 700 Pounds")
}
func (bike *Bike) Operate() {
fmt.Println("Riding Bike ............")
}
func (bike *Bike) Repair() {
fmt.Println("Bike repair")
}
func (bike *Bike) Start() {
fmt.Println("Bike started")
}
func (bike *Bike) Stop() {
fmt.Println("Bike stopped")
}
Car Struct
// car.go
package main
import "fmt"
type Car struct {
}
func NewCar() (car *Car) {
car = &Car{}
return
}
func (car *Car) GetInfo() {
fmt.Println("Transport type: Car")
fmt.Println("Number of wheels: 4")
fmt.Println("Average Weight: 4,000 Pounds")
}
func (car *Car) Operate() {
fmt.Println("Driving car ............")
}
func (car *Car) Repair() {
fmt.Println("Car repair")
}
func (car *Car) Start() {
fmt.Println("Car started")
}
func (car *Car) Stop() {
fmt.Println("Car stopped")
}
Plane Struct
// plane.go
package main
import "fmt"
type Plane struct {
}
func NewPlane() (plane *Plane) {
plane = &Plane{}
return
}
func (plane *Plane) GetInfo() {
fmt.Println("Transport type: Plane")
fmt.Println("Number of wheels: 3")
fmt.Println("Average Weight: 50,000 Pounds")
}
func (plane *Plane) Operate() {
fmt.Println("Flying plane ............")
}
func (plane *Plane) Repair() {
fmt.Println("Plane repair")
}
func (plane *Plane) Start() {
fmt.Println("Plane started")
}
func (plane *Plane) Stop() {
fmt.Println("Plane stopped")
}
Transport Strategy Struct
// transport_strategy.go
package main
type TransportStrategy struct {
transport Transport
}
func NewTransportStrategy(transport Transport) (rcvr *TransportStrategy) {
rcvr = &TransportStrategy{}
rcvr.transport = transport
return
}
func (rcvr *TransportStrategy) Execute() {
rcvr.transport.Start()
rcvr.transport.GetInfo()
rcvr.transport.Operate()
}
func (rcvr *TransportStrategy) Repair() {
rcvr.transport.Repair()
}
func (rcvr *TransportStrategy) Stop() {
rcvr.transport.Stop()
}
Demo
// main.go
package main
import "fmt"
func main() {
fmt.Println("Operating Bike:")
myTransport := NewTransportStrategy(NewBike())
myTransport.Execute()
myTransport.Stop()
fmt.Println("nnOperating Car:")
myTransport = NewTransportStrategy(NewCar())
myTransport.Execute()
myTransport.Stop()
myTransport.Repair()
fmt.Println("nnOperating plane:")
myTransport = NewTransportStrategy(NewPlane())
myTransport.Execute()
myTransport.Stop()
}
Output
Operating Bike:
Bike started
Transport type: Bike
Number of wheels: 2
Average Weight: 700 Pounds
Riding Bike ............
Bike stopped
Operating Car:
Car started
Transport type: Car
Number of wheels: 4
Average Weight: 4,000 Pounds
Driving car ............
Car stopped
Car repair
Operating plane:
Plane started
Transport type: Plane
Number of wheels: 3
Average Weight: 50,000 Pounds
Flying plane ............
Plane stopped
Example #2: Mathematical Calculations
Calculation Interface
// calculation.go
package main
type Calculation interface {
Execute(num1 float32, num2 float32) (float32)
}
Add Calculation Struct
// add.go
package main
type Add struct {}
func NewAdd() (add *Add) {
add = &Add{}
return
}
func (add *Add) Execute(num1 float32, num2 float32) (float32) {
return num1 + num2
}
Subtract Calculation Struct
// subtract.go
package main
type Subtract struct {}
func NewSubtract() (subtract *Subtract) {
subtract = &Subtract{}
return
}
func (subtract *Subtract) Execute(num1 float32, num2 float32) (float32) {
return num1 - num2
}
Multiply Calculation Struct
// multiply.go
package main
type Multiply struct {}
func NewMultiply() (rcvr *Multiply) {
rcvr = &Multiply{}
return
}
func (rcvr *Multiply) Execute(num1 float32, num2 float32) (float32) {
return num1 * num2
}
Divide Calculation Struct
// divide.go
package main
type Divide struct {}
func NewDivide() (divide *Divide) {
divide = &Divide{}
return
}
func (divide *Divide) Execute(num1 float32, num2 float32) (float32) {
return num1 / num2
}
Calculation Strategy Struct
// calculation_strategy.go
package main
type CalculationStrategy struct {
calculation Calculation
}
func NewCalculationStrategy(calculation Calculation) (calculationStrategy *CalculationStrategy) {
calculationStrategy = &CalculationStrategy{}
calculationStrategy.calculation = calculation
return
}
func (calculationStrategy *CalculationStrategy) Execute(num1 float32, num2 float32) (float32) {
return calculationStrategy.calculation.Execute(num1, num2)
}
Demo
// main.go
package main
import "fmt"
func main() {
fmt.Println("Performing operation: ADD")
myCalculation := NewCalculationStrategy(NewAdd())
result := myCalculation.Execute(10, 5)
fmt.Printf("10 + 5 = %vn", result)
fmt.Println("nnPerforming operation: SUBTRACT")
myCalculation = NewCalculationStrategy(NewSubtract())
result = myCalculation.Execute(10, 5)
fmt.Printf("10 - 5 = %vn", result)
fmt.Println("nnPerforming operation: MULTIPLY")
myCalculation = NewCalculationStrategy(NewMultiply())
result = myCalculation.Execute(10, 5)
fmt.Printf("10 * 5 = %vn", result)
fmt.Println("nnPerforming operation: DIVIDE")
myCalculation = NewCalculationStrategy(NewDivide())
result = myCalculation.Execute(10, 5)
fmt.Printf("10 / 5 = %vn", result)
}
Result
Performing operation: ADD
10 + 5 = 15
Performing operation: SUBTRACT
10 - 5 = 5
Performing operation: MULTIPLY
10 * 5 = 50
Performing operation: DIVIDE
10 / 5 = 2
Example #3: File Storage
Storage Interface
// storage.go
package main
type Storage interface {
PrintFileInfo(fileId int)
RetrieveFile(fileId int) string
StoreFile(tempPath string) int
}
Local Storage Struct
// local_storage.go
package main
import (
"fmt"
"math/rand"
)
type LocalStorage struct {
}
func NewLocalStorage() (localStorage *LocalStorage) {
localStorage = &LocalStorage{}
return
}
func (localStorage *LocalStorage) PrintFileInfo(fileId int) {
fmt.Println("Storage type: Local Storage")
fmt.Printf("File ID: %dn", fileId)
fmt.Printf("File URL: %sn", localStorage.RetrieveFile(fileId))
}
func (localStorage *LocalStorage) RetrieveFile(fileId int) string {
// Retrieve the url and return back
// Some dummy url is returned for demo purpose
return fmt.Sprintf("https://bigboxcode.com/files/local/%d", fileId)
}
func (localStorage *LocalStorage) StoreFile(tempPath string) int {
// Code to store file here
// return the ID that is obtained from the database record or any other unique identifier.
// For demo purpose a random number is returned here
return rand.Intn(100)
}
Google Drive Storage Struct
// google_drive_storage.go
package main
import (
"fmt"
"math/rand"
)
type GoogleDriveStorage struct {
}
func NewGoogleDriveStorage() (googleDriveStorage *GoogleDriveStorage) {
googleDriveStorage = &GoogleDriveStorage{}
return
}
func (googleDriveStorage *GoogleDriveStorage) PrintFileInfo(fileId int) {
fmt.Println("Storage type: Google Drive")
fmt.Printf("File ID: %dn", fileId)
fmt.Printf("File URL: %sn", googleDriveStorage.RetrieveFile(fileId))
}
func (googleDriveStorage *GoogleDriveStorage) RetrieveFile(fileId int) string {
// Retrieve the url and return back
// Some dummy url is returned for demo purpose
return "https://drive.google.com/file/d/1234_9K7654hu6RT_9j7JKY3fK/view"
}
func (googleDriveStorage *GoogleDriveStorage) StoreFile(tempPath string) int {
// Code to store file here
// return the ID that is obtained from the database record or any other unique identifier.
// For demo purpose a random number is returned here
return rand.Intn(1000)
}
AWS S3 Storage Struct
// s3_storage.go
package main
import (
"fmt"
"math/rand"
)
type S3Storage struct {
}
func NewS3Storage() (s3Storage *S3Storage) {
s3Storage = &S3Storage{}
return
}
func (s3Storage *S3Storage) PrintFileInfo(fileId int) {
fmt.Println("Storage type: AWS S3")
fmt.Printf("File ID: %dn", fileId)
fmt.Printf("File URL: %sn", s3Storage.RetrieveFile(fileId))
}
func (s3Storage *S3Storage) RetrieveFile(fileId int) string {
// Retrieve the url and return back
// Some dummy url is returned for demo purpose
return "https://bigboxcode.s3.amazonaws.com/pdf/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf"
}
func (s3Storage *S3Storage) StoreFile(tempPath string) int {
// Code to store file here
// return the ID that is obtained from the database record or any other unique identifier.
// For demo purpose a random number is returned here
return rand.Intn(10000)
}
Storage Strategy Struct
// storage_strategy.go
package main
type StorageStrategy struct {
storage Storage
}
func NewStorageStrategy(storage Storage) (storageStrategy *StorageStrategy) {
storageStrategy = &StorageStrategy{}
storageStrategy.storage = storage
return
}
func (storageStrategy *StorageStrategy) GetFileUrl(fileId int) string {
return storageStrategy.storage.RetrieveFile(fileId)
}
func (storageStrategy *StorageStrategy) UploadFile(tempPath string) int {
fileId := storageStrategy.storage.StoreFile(tempPath)
storageStrategy.storage.PrintFileInfo(fileId)
return fileId
}
Demo
// main.go
package main
import "fmt"
func main() {
// Use Local storage
fmt.Println("Using local storage:")
fileStorage := NewStorageStrategy(NewLocalStorage())
fileStorage.UploadFile("/some-temp-path")
// Use car
fmt.Println("nnUsing AWS S3:")
fileStorage = NewStorageStrategy(NewS3Storage())
fileStorage.UploadFile("/some-temp-path")
// Use plane
fmt.Println("nnUsing Google Drive:")
fileStorage = NewStorageStrategy(NewGoogleDriveStorage())
fileStorage.UploadFile("/some-temp-path")
}
Output
Using local storage:
Storage type: Local Storage
File ID: 29
File URL: https://bigboxcode.com/files/local/29
Using AWS S3:
Storage type: AWS S3
File ID: 1437
File URL: https://bigboxcode.s3.amazonaws.com/pdf/UC-0e7654338-5697-4f99-b33-d89h87g5gf4gwfg.pdf
Using Google Drive:
Storage type: Google Drive
File ID: 68
Source Code
Use the following link to get the source code:
Example | Source Code Link |
---|---|
Example #1: Transport | GitHub |
Example #2: Mathematical Calculations | GitHub |
Example #3: File Storage | GitHub |
Other Code Implementations
Use the following links to check Strategy pattern implementation in other programming languages.