Menu
Coddy logo textTech

Patrón Strategy

Parte de la sección Programación Orientada a Objetos del Journey de GO de Coddy — lección 90 de 107.

El patrón Strategy le permite definir una familia de algoritmos, encapsular cada uno de ellos y hacerlos intercambiables en tiempo de ejecución. A diferencia de Observer, donde múltiples objetos reaccionan a un evento, Strategy permite que un solo objeto cambie entre diferentes comportamientos de forma dinámica.

En Go, implementamos el patrón Strategy definiendo una interfaz para el algoritmo e inyectando diferentes implementaciones en un struct de contexto:

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)
}

La estructura context mantiene una referencia a la interfaz strategy, lo que permite intercambiar el algoritmo sin cambiar el código del contexto:

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)
}

Ahora puedes cambiar el método de pago en tiempo de ejecución:

checkout := &Checkout{}

checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00))  // Pagado 100.00 vía Tarjeta de Crédito

checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00))   // Pagado 50.00 vía PayPal

Strategy es ideal cuando tienes múltiples formas de realizar una operación y necesitas seleccionar una basada en la entrada del usuario, la configuración o las condiciones de tiempo de ejecución. Los casos de uso comunes incluyen algoritmos de ordenamiento, métodos de compresión y reglas de validación.

challenge icon

Desafío

Fácil

¡Vamos a construir un sistema de formateo de texto utilizando el patrón Strategy! Crearás diferentes estrategias de formateo que se pueden intercambiar en tiempo de ejecución, permitiendo que el mismo procesador de texto produzca diferentes estilos de salida sin cambiar su código principal.

Organizarás tu código en tres archivos:

  • formatter.go: Define tu interfaz de estrategia y las estrategias de formateo concretas.

    Crea una interfaz TextFormatter con un método Format(text string) string que transforme el texto según las reglas de la estrategia.

    Implementa tres estrategias de formateo:

    • UppercaseFormatter — convierte el texto a mayúsculas
    • SnakeCaseFormatter — convierte los espacios en guiones bajos y pone el texto en minúsculas
    • TitleFormatter — pone en mayúscula la primera letra de cada palabra (las palabras están separadas por espacios)
  • processor.go: Crea tu estructura de contexto que utiliza la estrategia de formateo.

    Construye una estructura TextProcessor que contenga una estrategia TextFormatter. Añade estos métodos:

    • SetFormatter(f TextFormatter) para cambiar la estrategia de formateo activa
    • Process(text string) string que aplica el formateador actual al texto

    También crea un constructor NewTextProcessor() que devuelva un procesador sin ningún formateador inicial configurado.

  • main.go: Demuestra el intercambio de estrategias en tiempo de ejecución.

    Lee un fragmento de texto, luego lee un recuento de operaciones de formato. Para cada operación, lee el tipo de formato (upper, snake, o title), establece el formateador apropiado en tu procesador, procesa el texto original e imprime el resultado.

Se proporcionarán las siguientes entradas:

  • Línea 1: El texto a formatear
  • Línea 2: Número de operaciones de formato
  • Líneas siguientes: Tipo de formato para cada operación

Por ejemplo, dado:

hello world today
3
upper
snake
title

Tu salida debería ser:

HELLO WORLD TODAY
hello_world_today
Hello World Today

Y dado:

Go Programming Language
2
snake
upper

Tu salida debería ser:

go_programming_language
GO PROGRAMMING LANGUAGE

Y dado:

design patterns are useful
1
title

Tu salida debería ser:

Design Patterns Are Useful

Observa cómo el mismo TextProcessor produce salidas completamente diferentes simplemente intercambiando su estrategia de formateador. El procesador no sabe ni le importa qué formateador específico está utilizando; ¡simplemente delega en cualquier estrategia que esté configurada actualmente!

Hoja de referencia

El patrón Strategy define una familia de algoritmos, encapsula cada uno de ellos y los hace intercambiables en tiempo de ejecución. Permite que un solo objeto cambie entre diferentes comportamientos de forma dinámica.

Define una interfaz para el algoritmo:

type PaymentStrategy interface {
    Pay(amount float64) string
}

Implementa estrategias concretas:

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)
}

Crea una estructura de contexto que mantenga una referencia a la interfaz de la estrategia:

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)
}

Cambia las estrategias en tiempo de ejecución:

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 es ideal cuando tienes múltiples formas de realizar una operación y necesitas seleccionar una basada en la entrada del usuario, la configuración o las condiciones en tiempo de ejecución.

Pruébalo tú mismo

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Leer el texto a formatear
	text, _ := reader.ReadString('\n')
	text = strings.TrimSpace(text)
	
	// Leer el número de operaciones
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))
	
	// Crear un nuevo procesador de texto
	processor := NewTextProcessor()
	
	// Procesar cada operación de formato
	for i := 0; i < count; i++ {
		formatType, _ := reader.ReadString('\n')
		formatType = strings.TrimSpace(formatType)
		
		// TODO: Basado en formatType ("upper", "snake", o "title"),
		// establecer el formateador apropiado en el procesador
		// e imprimir el texto procesado
		
	}
}
quiz iconPonte a prueba

Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.

Todas las lecciones de Programación Orientada a Objetos