Menu
Coddy logo textTech

Padrão de Estado

Parte da seção Programação Orientada a Objetos do Journey de GO da Coddy. Lição 95 de 107.

O padrão State permite que um objeto altere seu comportamento quando seu estado interno muda, fazendo parecer que o objeto mudou de classe. Enquanto o Template Method controla as etapas do algoritmo, o State encapsula o comportamento específico de cada estado em objetos separados e delega ao estado atual.

Em Go, definimos uma interface de estado e criamos estados concretos que implementam comportamentos diferentes:

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

Cada estado determina o que acontece e qual estado vem a seguir:

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

A mesma chamada de método produz resultados diferentes com base no estado atual:

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

fmt.Println(doc.Publish())  // Rascunho enviado para moderação
fmt.Println(doc.Publish())  // Moderação aprovada, agora publicado
fmt.Println(doc.Publish())  // Já publicado

State é ideal para objetos com modos de operação distintos, como fluxos de processamento de pedidos, componentes de UI ou manipuladores de conexão, nos quais o comportamento depende inteiramente do estado atual.

challenge icon

Desafio

Fácil

Vamos criar um sistema de suporte de tickets usando o padrão State! Você criará um ticket de suporte que passa por diferentes etapas, desde a abertura, passando pelo atendimento, até a resolução, com cada estado determinando quais ações são possíveis e o que acontece em seguida.

Você organizará seu código em três arquivos:

  • state.go: defina sua interface de estado e os estados concretos que representam cada etapa do ciclo de vida de um ticket.

    Crie uma interface TicketState com um método Handle(t *Ticket) string que processe o ticket e potencialmente faça a transição para o próximo estado.

    Implemente três estados:

    • OpenState: quando processado, faz a transição do ticket para InProgressState e retorna Ticket opened, assigning to support team
    • InProgressState: quando processado, faz a transição para ResolvedState e retorna Working on ticket, issue resolved
    • ResolvedState: quando processado, permanece no mesmo estado e retorna Ticket already resolved
  • ticket.go: crie sua struct de ticket, que mantém o estado atual e delega o comportamento a ele.

    Crie uma struct Ticket com um campo ID (string) e um campo state (TicketState). Adicione estes métodos:

    • SetState(s TicketState) para alterar o estado atual do ticket
    • Process() string que delega ao método Handle do estado atual

    Crie um construtor NewTicket(id string) *Ticket que retorne um ticket iniciado no estado OpenState.

  • main.go: demonstre como a mesma ação produz resultados diferentes com base no estado atual do ticket.

    Leia um ID de ticket e o número de vezes que o ticket deve ser processado. Crie um novo ticket com esse ID e, em seguida, chame Process() o número de vezes especificado, imprimindo cada resultado em uma linha separada.

As seguintes entradas serão fornecidas:

  • Linha 1: ID do ticket
  • Linha 2: número de vezes que o ticket deve ser processado

Por exemplo, dada a entrada:

TKT-001
3

Sua saída deve ser:

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

E dada a entrada:

TKT-500
5

Sua saída deve ser:

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

E dada a entrada:

ISSUE-42
1

Sua saída deve ser:

Ticket opened, assigning to support team

Observe como chamar Process() no mesmo ticket produz resultados diferentes a cada vez: o comportamento do ticket muda à medida que ele passa pelos estados. Quando é resolvido, ele permanece resolvido, independentemente de quantas vezes você o processe. O objeto ticket parece mudar seu comportamento, mas na verdade está delegando a diferentes objetos de estado!

Experimente você mesmo

package main

import (
	"fmt"
)

func main() {
	// Ler entrada
	var ticketID string
	var numProcesses int
	fmt.Scanln(&ticketID)
	fmt.Scanln(&numProcesses)

	// TODO: Criar um novo ticket com o ID fornecido

	// TODO: Processar o ticket o número especificado de vezes
	// e imprimir cada resultado em uma linha separada
}
quiz iconTeste seus conhecimentos

Esta lição inclui um quiz rápido. Comece a lição para respondê-lo e acompanhar seu progresso.

Todas as lições de Programação Orientada a Objetos

Pratique por conta própria: Compilador de Go online