Menu
Coddy logo textTech

Pattern State

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

Il pattern State consente a un oggetto di cambiare comportamento quando cambia il suo stato interno, facendo sembrare che l'oggetto abbia cambiato classe. Mentre il Template Method controlla i passaggi dell'algoritmo, State incapsula i comportamenti specifici dello stato in oggetti separati e delega allo stato corrente.

In Go, definiamo un'interfaccia di stato e creiamo stati concreti che implementano comportamenti diversi:

type State interface {
    Handle(d *Document) string
}

type Document struct {
    state State
}

func (d *Document) SetState(s State) {
    d.state = s
}

func (d *Document) Publish() string {
    return d.state.Handle(d)
}

Ogni stato determina cosa succede e quale stato viene dopo:

type DraftState struct{}

func (s DraftState) Handle(d *Document) string {
    d.SetState(ModerationState{})
    return "Draft submitted for moderation"
}

type ModerationState struct{}

func (s ModerationState) Handle(d *Document) string {
    d.SetState(PublishedState{})
    return "Moderation approved, now published"
}

type PublishedState struct{}

func (s PublishedState) Handle(d *Document) string {
    return "Already published"
}

La stessa chiamata al metodo produce risultati diversi in base allo stato corrente:

doc := &Document{state: DraftState{}}

fmt.Println(doc.Publish())  // Bozza inviata per la moderazione
fmt.Println(doc.Publish())  // Moderazione approvata, ora pubblicato
fmt.Println(doc.Publish())  // Già pubblicato

Lo stato è ideale per gli oggetti con modalità operative distinte, come i flussi di lavoro di elaborazione degli ordini, i componenti dell’interfaccia utente o i gestori delle connessioni, in cui il comportamento dipende interamente dallo stato corrente.

challenge icon

Sfida

Facile

Costruiamo un sistema di assistenza per ticket usando il pattern State! Creerai un ticket di assistenza che attraversa diverse fasi: viene aperto, preso in carico e risolto. Ogni stato determina quali azioni sono possibili e cosa succede dopo.

Organizzerai il codice in tre file:

  • state.go: definisci l'interfaccia degli stati e gli stati concreti che rappresentano ciascuna fase del ciclo di vita di un ticket.

    Crea un'interfaccia TicketState con un metodo Handle(t *Ticket) string che elabora il ticket e, potenzialmente, lo fa passare allo stato successivo.

    Implementa tre stati:

    • OpenState: quando viene gestito, fa passare il ticket a InProgressState e restituisce Ticket opened, assigning to support team
    • InProgressState: quando viene gestito, fa passare il ticket a ResolvedState e restituisce Working on ticket, issue resolved
    • ResolvedState: quando viene gestito, mantiene il ticket nello stesso stato e restituisce Ticket already resolved
  • ticket.go: crea la struct del ticket, che contiene lo stato corrente e delega a esso il comportamento.

    Crea una struct Ticket con un campo ID (string) e un campo state (TicketState). Aggiungi questi metodi:

    • SetState(s TicketState) per cambiare lo stato corrente del ticket
    • Process() string che delega al metodo Handle dello stato corrente

    Crea un costruttore NewTicket(id string) *Ticket che restituisce un ticket inizializzato nello stato OpenState.

  • main.go: mostra come la stessa azione produca risultati diversi in base allo stato corrente del ticket.

    Leggi un ID del ticket e il numero di volte in cui elaborarlo. Crea un nuovo ticket con quell'ID, poi chiama Process() il numero di volte specificato, stampando ogni risultato su una riga separata.

Verranno forniti i seguenti input:

  • Riga 1: ID del ticket
  • Riga 2: numero di volte in cui elaborare il ticket

Ad esempio, dato:

TKT-001
3

L'output dovrebbe essere:

Ticket opened, assigning to support team
Working on ticket, issue resolved
Ticket already resolved

E dato:

TKT-500
5

L'output dovrebbe essere:

Ticket opened, assigning to support team
Working on ticket, issue resolved
Ticket already resolved
Ticket already resolved
Ticket already resolved

E dato:

ISSUE-42
1

L'output dovrebbe essere:

Ticket opened, assigning to support team

Nota come chiamare Process() sullo stesso ticket produca ogni volta risultati diversi: il comportamento del ticket cambia man mano che attraversa gli stati. Una volta risolto, rimane tale indipendentemente da quante volte lo elabori. L'oggetto ticket sembra cambiare comportamento, ma in realtà delega a oggetti di stato diversi!

Provalo tu

package main

import (
	"fmt"
)

func main() {
	// Leggi l'input
	var ticketID string
	var numProcesses int
	fmt.Scanln(&ticketID)
	fmt.Scanln(&numProcesses)

	// TODO: Crea un nuovo ticket con l'ID fornito

	// TODO: Elabora il ticket il numero di volte specificato
	// e stampa ogni risultato su una riga separata
}
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