Menu
Coddy logo textTech

Strategy Pattern

Part of the Object Oriented Programming section of Coddy's GO journey — lesson 90 of 107.

The Strategy pattern allows you to define a family of algorithms, encapsulate each one, and make them interchangeable at runtime. Unlike Observer where multiple objects react to one event, Strategy lets a single object switch between different behaviors dynamically.

In Go, we implement Strategy by defining an interface for the algorithm and injecting different implementations into a context struct:

type PaymentStrategy interface {
    Pay(amount float64) string
}

type CreditCard struct{}
func (c CreditCard) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}

type PayPal struct{}
func (p PayPal) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via PayPal", amount)
}

The context struct holds a reference to the strategy interface, allowing the algorithm to be swapped without changing the context's code:

type Checkout struct {
    strategy PaymentStrategy
}

func (c *Checkout) SetStrategy(s PaymentStrategy) {
    c.strategy = s
}

func (c *Checkout) ProcessPayment(amount float64) string {
    return c.strategy.Pay(amount)
}

Now you can change the payment method at runtime:

checkout := &Checkout{}

checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00))  // Paid 100.00 via Credit Card

checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00))   // Paid 50.00 via PayPal

Strategy is ideal when you have multiple ways to perform an operation and need to select one based on user input, configuration, or runtime conditions. Common use cases include sorting algorithms, compression methods, and validation rules.

challenge icon

Challenge

Easy

Let's build a text formatting system using the Strategy pattern! You'll create different formatting strategies that can be swapped at runtime, allowing the same text processor to produce different output styles without changing its core code.

You'll organize your code across three files:

  • formatter.go: Define your strategy interface and concrete formatting strategies.

    Create a TextFormatter interface with a method Format(text string) string that transforms text according to the strategy's rules.

    Implement three formatting strategies:

    • UppercaseFormatter — converts text to all uppercase
    • SnakeCaseFormatter — converts spaces to underscores and makes text lowercase
    • TitleFormatter — capitalizes the first letter of each word (words are separated by spaces)
  • processor.go: Create your context struct that uses the formatting strategy.

    Build a TextProcessor struct that holds a TextFormatter strategy. Add these methods:

    • SetFormatter(f TextFormatter) to change the active formatting strategy
    • Process(text string) string that applies the current formatter to the text

    Also create a NewTextProcessor() constructor that returns a processor with no initial formatter set.

  • main.go: Demonstrate switching strategies at runtime.

    Read a piece of text, then read a count of format operations. For each operation, read the format type (upper, snake, or title), set the appropriate formatter on your processor, process the original text, and print the result.

The following inputs will be provided:

  • Line 1: The text to format
  • Line 2: Number of format operations
  • Following lines: Format type for each operation

For example, given:

hello world today
3
upper
snake
title

Your output should be:

HELLO WORLD TODAY
hello_world_today
Hello World Today

And given:

Go Programming Language
2
snake
upper

Your output should be:

go_programming_language
GO PROGRAMMING LANGUAGE

And given:

design patterns are useful
1
title

Your output should be:

Design Patterns Are Useful

Notice how the same TextProcessor produces completely different outputs just by swapping its formatter strategy. The processor doesn't know or care which specific formatter it's using—it simply delegates to whatever strategy is currently set!

Cheat sheet

The Strategy pattern defines a family of algorithms, encapsulates each one, and makes them interchangeable at runtime. It allows a single object to switch between different behaviors dynamically.

Define an interface for the algorithm:

type PaymentStrategy interface {
    Pay(amount float64) string
}

Implement concrete strategies:

type CreditCard struct{}
func (c CreditCard) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via Credit Card", amount)
}

type PayPal struct{}
func (p PayPal) Pay(amount float64) string {
    return fmt.Sprintf("Paid %.2f via PayPal", amount)
}

Create a context struct that holds a reference to the strategy interface:

type Checkout struct {
    strategy PaymentStrategy
}

func (c *Checkout) SetStrategy(s PaymentStrategy) {
    c.strategy = s
}

func (c *Checkout) ProcessPayment(amount float64) string {
    return c.strategy.Pay(amount)
}

Change strategies at runtime:

checkout := &Checkout{}

checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00))  // Paid 100.00 via Credit Card

checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00))   // Paid 50.00 via PayPal

Strategy is ideal when you have multiple ways to perform an operation and need to select one based on user input, configuration, or runtime conditions.

Try it yourself

package main

import (
	"bufio"
	"fmt"
	"os"
	"strconv"
	"strings"
)

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Read the text to format
	text, _ := reader.ReadString('\n')
	text = strings.TrimSpace(text)
	
	// Read the number of operations
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))
	
	// Create a new text processor
	processor := NewTextProcessor()
	
	// Process each format operation
	for i := 0; i < count; i++ {
		formatType, _ := reader.ReadString('\n')
		formatType = strings.TrimSpace(formatType)
		
		// TODO: Based on formatType ("upper", "snake", or "title"),
		// set the appropriate formatter on the processor
		// and print the processed text
		
	}
}
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming