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 PayPalLa 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.
Sfida
FacileCostruiamo 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
TextFormattercon un metodoFormat(text string) stringche trasforma il testo secondo le regole della strategia.Implementa tre strategie di formattazione:
UppercaseFormatter: converte il testo interamente in maiuscoloSnakeCaseFormatter: converte gli spazi in trattini bassi e trasforma il testo in minuscoloTitleFormatter: 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
TextProcessorche contiene una strategiaTextFormatter. Aggiungi questi metodi:SetFormatter(f TextFormatter)per cambiare la strategia di formattazione attivaProcess(text string) stringche 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,snakeotitle), 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
titleL'output dovrebbe essere:
HELLO WORLD TODAY
hello_world_today
Hello World TodayE dato:
Go Programming Language
2
snake
upperL'output dovrebbe essere:
go_programming_language
GO PROGRAMMING LANGUAGEE dato:
design patterns are useful
1
titleL'output dovrebbe essere:
Design Patterns Are UsefulNota 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
}
}
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
1Fondamenti di OOP in Go
File esterniWorkspace e moduli GoPacchetti e importazioniNomi esportati e non esportatiIntroduzione all’OOP in GoGli struct come classiDefinire metodi sugli structReceiver con puntatore vs receiver con valoreInizializzazione degli structFunzioni costruttriciRiepilogo - Calcolatrice semplice4Interfacce
Introduzione alle interfacceImplementazione implicitaL’interfaccia come contrattoInterfaccia vuota (any)Asserzione di tipoSwitch sul tipoComposizione di interfacceInterfacce Stringer ed ErrorRipasso - Calcolatore di forme7Incapsulamento
Campi esportati e non esportatiIncapsulamento a livello di packageMetodi Getter e SetterNascondimento delle informazioni in GoRiepilogo - Registri degli studenti10Tipi generici (Go 1.18+)
Introduzione ai tipi genericiParametri di tipoVincoli di tipoStruct genericheSoluzione alternativa per i metodi genericiRiepilogo - Collezione generica13Modelli di progettazione Parte 1
Introduzione ai modelli di progettazioneModello SingletonModello FactoryModello Abstract FactoryModello ObserverModello Strategy2Approfondimento su tipi e struct
Tipi di base e compostiDefinizioni di tipi personalizzatiTag delle structStruct anonimeStruct annidateValori zero e predefinitiRipasso - Rubrica5Composizione invece dell'ereditarietà
Perché Go non ha l'ereditarietàFondamenti dell'embedding delle structPromozione dei metodiEmbedding di più structEmbedding e aggregazione a confrontoMascheramento dei metodi embeddedRiepilogo: gerarchia dei dipendenti8Gestione degli errori e OOP
L'interfaccia errorTipi di errore personalizzatiWrapping degli errori (fmt.Errorf)Errori sentinellaerrors.Is() ed errors.As()Panic, Defer e RecoverRiepilogo - Parser di file11Libreria standard e OOP
io.Reader e io.Writersort.InterfaceInterfaccia fmt.Stringerencoding/json con le structInterfaccia http.HandlerRipasso - modelli REST API14Modelli di progettazione – Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeMiddleware come Decorator3Puntatori e memoria
Concetti base dei puntatori in GoPuntatori a structPassaggio per valore vs per riferimentoLa funzione new()Garbage collection in GoRiepilogo - Costruttore di liste concatenate6Polimorfismo in Go
Polimorfismo tramite interfacceDuck typing in GoRegole di soddisfacimento delle interfacceCollezioni polimorficheIniezione delle dipendenzeRiepilogo - Elaboratore di pagamenti9Concorrenza e OOP
Basi delle goroutineCanali e comunicazioneCanali bufferizzati vs non bufferizzatiIstruzione selectsync.Mutex e sync.RWMutexsync.WaitGroupProgettazione di struct thread-safeRiepilogo: worker poolEsercitati da solo: Compilatore Go online