Menu
Coddy logo textTech

Modello Strategy

Fa parte della sezione Programmazione orientata agli oggetti del percorso GO di Coddy. Lezione 90 di 107.

Il pattern Strategy ti consente di definire una famiglia di algoritmi, incapsularli singolarmente e renderli intercambiabili durante l’esecuzione. A differenza di Observer, in cui più oggetti reagiscono a un singolo evento, Strategy permette a un singolo oggetto di passare dinamicamente da un comportamento all’altro.

In Go, implementiamo Strategy definendo un'interfaccia per l'algoritmo e iniettando diverse implementazioni in una struct di contesto:

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 struct del contesto contiene un riferimento all’interfaccia della strategia, consentendo di sostituire l’algoritmo senza modificare il codice del contesto:

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

Ora puoi cambiare il metodo di pagamento durante l'esecuzione:

checkout := &Checkout{}

checkout.SetStrategy(CreditCard{})
fmt.Println(checkout.ProcessPayment(100.00))  // Pagato 100.00 con carta di credito

checkout.SetStrategy(PayPal{})
fmt.Println(checkout.ProcessPayment(50.00))   // Pagato 50.00 con PayPal

La strategia è ideale quando hai diversi modi per eseguire un'operazione e devi selezionarne uno in base all'input dell'utente, alla configurazione o alle condizioni di runtime. I casi d'uso comuni includono algoritmi di ordinamento, metodi di compressione e regole di convalida.

challenge icon

Sfida

Facile

Costruiamo un sistema di formattazione del testo usando il pattern Strategy! Creerai diverse strategie di formattazione che possono essere sostituite durante l'esecuzione, consentendo allo stesso elaboratore di testo di produrre stili di output diversi senza modificarne il codice principale.

Organizzerai il codice in tre file:

  • formatter.go: definisci l'interfaccia della strategia e le strategie di formattazione concrete.

    Crea un'interfaccia TextFormatter con un metodo Format(text string) string che trasforma il testo secondo le regole della strategia.

    Implementa tre strategie di formattazione:

    • UppercaseFormatter: converte il testo interamente in maiuscolo
    • SnakeCaseFormatter: converte gli spazi in trattini bassi e trasforma il testo in minuscolo
    • TitleFormatter: mette in maiuscolo la prima lettera di ogni parola (le parole sono separate da spazi)
  • processor.go: crea la struct di contesto che usa la strategia di formattazione.

    Crea una struct TextProcessor che contiene una strategia TextFormatter. Aggiungi questi metodi:

    • SetFormatter(f TextFormatter) per cambiare la strategia di formattazione attiva
    • Process(text string) string che applica al testo il formattatore corrente

    Crea anche un costruttore NewTextProcessor() che restituisce un elaboratore senza alcun formattatore iniziale impostato.

  • main.go: mostra come cambiare strategia durante l'esecuzione.

    Leggi un testo, poi leggi il numero di operazioni di formattazione. Per ogni operazione, leggi il tipo di formattazione (upper, snake o title), imposta il formattatore appropriato nell'elaboratore, elabora il testo originale e stampa il risultato.

Verranno forniti i seguenti input:

  • Riga 1: il testo da formattare
  • Riga 2: numero di operazioni di formattazione
  • Righe successive: tipo di formattazione per ogni operazione

Per esempio, dato:

hello world today
3
upper
snake
title

L'output dovrebbe essere:

HELLO WORLD TODAY
hello_world_today
Hello World Today

E dato:

Go Programming Language
2
snake
upper

L'output dovrebbe essere:

go_programming_language
GO PROGRAMMING LANGUAGE

E dato:

design patterns are useful
1
title

L'output dovrebbe essere:

Design Patterns Are Useful

Nota come lo stesso TextProcessor produca output completamente diversi semplicemente sostituendo la strategia del formattatore. All'elaboratore non interessa quale formattatore specifico stia usando. Si limita a delegare alla strategia impostata in quel momento!

Provalo tu

package main

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

func main() {
	reader := bufio.NewReader(os.Stdin)
	
	// Leggi il testo da formattare
	text, _ := reader.ReadString('\n')
	text = strings.TrimSpace(text)
	
	// Leggi il numero di operazioni
	countStr, _ := reader.ReadString('\n')
	count, _ := strconv.Atoi(strings.TrimSpace(countStr))
	
	// Crea un nuovo processore di testo
	processor := NewTextProcessor()
	
	// Esegui ogni operazione di formattazione
	for i := 0; i < count; i++ {
		formatType, _ := reader.ReadString('\n')
		formatType = strings.TrimSpace(formatType)
		
		// TODO: In base a formatType ("upper", "snake" o "title"),
		// imposta il formattatore appropriato sul processore
		// e stampa il testo elaborato
		
	}
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore Go online