Patrón State
Parte de la sección Programación Orientada a Objetos del Journey de Java de Coddy — lección 74 de 87.
El Patrón de Estado (State Pattern) es un patrón de diseño de comportamiento que permite que un objeto cambie su comportamiento cuando su estado interno cambia. El objeto parece cambiar su clase, pero en realidad, delega el comportamiento a diferentes objetos de estado. Piense en una máquina expendedora: su respuesta al presionar un botón depende de si está inactiva, si se ha insertado dinero o si está dispensando un producto.
En lugar de utilizar sentencias condicionales complejas para manejar diferentes estados, encapsulas cada estado en su propia clase. El patrón involucra un Context que mantiene una referencia al estado actual, y clases State que definen el comportamiento para cada estado:
interface State {
void handle(Context context);
}
class Context {
private State state;
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}Cada estado concreto implementa el comportamiento y puede activar transiciones a otros estados:
class IdleState implements State {
public void handle(Context context) {
System.out.println("Idle: Insert coin to start");
context.setState(new ActiveState());
}
}
class ActiveState implements State {
public void handle(Context context) {
System.out.println("Active: Processing...");
context.setState(new IdleState());
}
}El contexto delega las peticiones a su estado actual, y el comportamiento cambia automáticamente a medida que el estado cambia:
Context context = new Context();
context.setState(new IdleState());
context.request(); // Inactivo: Inserte moneda para comenzar
context.request(); // Activo: Procesando...El patrón State es ideal cuando el comportamiento de un objeto depende en gran medida de su estado y debe cambiar en tiempo de ejecución. Elimina grandes bloques condicionales y hace que añadir nuevos estados sea sencillo: solo hay que crear una nueva clase que implemente la interfaz State.
Desafío
Fácil¡Vamos a construir un sistema de flujo de trabajo de documentos utilizando el Patrón State! Crearás un sistema que modela cómo un documento se mueve a través de diferentes etapas—Draft, Review y Published—donde cada estado determina qué acciones están disponibles y cómo se comporta el documento cuando intentas editarlo o aprobarlo.
Organizarás tu código en cuatro archivos:
DocumentState.java: Define la interfazDocumentStateque implementarán todos los estados. Debe declarar dos métodos:edit(Document doc)para intentar editar el documento, yapprove(Document doc)para intentar aprobar/avanzar el documento a la siguiente etapa.States.java: Crea tres clases de estado concretas que implementenDocumentState:DraftState- Cuando se llama aedit, imprimeEditing draft.... Cuando se llama aapprove, imprimeDraft approved. Moving to review.y transiciona el documento aReviewState.ReviewState- Cuando se llama aedit, imprimeCannot edit during review.(sin cambio de estado). Cuando se llama aapprove, imprimeReview complete. Publishing document.y transiciona aPublishedState.PublishedState- Cuando se llama aedit, imprimeCannot edit published document.. Cuando se llama aapprove, imprimeDocument already published.. Ninguna acción cambia el estado.Document.java: Crea la clase de contexto que mantiene el estado actual. TuDocumentdebe tener:Un campo privado que contenga el
DocumentStateactual, inicializado aDraftStateen el constructor.Un método
setState(DocumentState state)que cambie el estado actual.Un método
edit()que delegue en el métodoeditdel estado actual.Un método
approve()que delegue en el métodoapprovedel estado actual.Un método
getStatus()que devuelva el nombre simple de la clase del estado actual (usagetClass().getSimpleName()).Main.java: ¡Demuestra tu sistema de estados! Recibirás una entrada: una secuencia de acciones como una cadena separada por comas donde cada acción eseditoapprove(por ejemplo:edit,approve,edit,approve).Crea un
Documente imprime su estado inicial en el formatoStatus: [state]. Luego procesa cada acción en orden, llamando al método apropiado en el documento. Después de cada acción, imprime el estado actual.
Recibirás una entrada: una cadena de acciones separada por comas.
Por ejemplo, con la entrada edit,approve,approve, tu salida sería:
Status: DraftState
Editing draft...
Status: DraftState
Draft approved. Moving to review.
Status: ReviewState
Review complete. Publishing document.
Status: PublishedStateObserva cómo la misma llamada a approve() produce resultados diferentes dependiendo del estado actual del documento—¡esta es la esencia del Patrón State! El documento no utiliza condicionales para verificar su estado; en su lugar, delega el comportamiento al objeto de estado actual, que maneja la acción de manera apropiada y activa las transiciones cuando es necesario.
Hoja de referencia
El Patrón State es un patrón de diseño de comportamiento que permite que un objeto cambie su comportamiento cuando su estado interno cambia, delegando el comportamiento a diferentes objetos de estado en lugar de utilizar sentencias condicionales complejas.
El patrón consiste en un Contexto que mantiene una referencia al estado actual, y clases de Estado que definen el comportamiento para cada estado:
interface State {
void handle(Context context);
}
class Context {
private State state;
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}Cada estado concreto implementa el comportamiento y puede activar transiciones a otros estados:
class IdleState implements State {
public void handle(Context context) {
System.out.println("Idle: Insert coin to start");
context.setState(new ActiveState());
}
}
class ActiveState implements State {
public void handle(Context context) {
System.out.println("Active: Processing...");
context.setState(new IdleState());
}
}El contexto delega las peticiones a su estado actual, y el comportamiento cambia automáticamente a medida que cambia el estado:
Context context = new Context();
context.setState(new IdleState());
context.request(); // Idle: Insert coin to start
context.request(); // Active: Processing...El Patrón State es ideal cuando el comportamiento de un objeto depende en gran medida de su estado y debe cambiar en tiempo de ejecución. Elimina grandes bloques condicionales y hace que añadir nuevos estados sea sencillo.
Pruébalo tú mismo
import java.util.Scanner;
class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
// TODO: Crear un nuevo Documento
// TODO: Imprimir el estado inicial en formato "Status: [state]"
// TODO: Dividir la entrada por coma para obtener acciones individuales
// TODO: Recorrer cada acción
// - Si la acción es "edit", llamar a edit() en el documento
// - Si la acción es "approve", llamar a approve() en el documento
// - Después de cada acción, imprimir el estado actual
}
}
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
1Fundamentos de OOP
Archivos externosIntroducción a la OOPClases vs. ObjetosLa palabra clave thisMétodosCampos (Atributos)Método constructorSobrecarga de constructoresResumen - Calculadora simple4Herencia
Herencia básica (extends)La palabra clave superSobrescritura de métodos (@Override)Encadenamiento de constructoresLa clase ObjectHerencia simple y multinivelPor qué no hay herencia múltiple de clasesResumen - Jerarquía de empleados7Métodos especiales y la clase Object
Método toString()equals() y hashCode()Método clone()compareTo() y ComparableInterfaz ComparatorResumen - Ordenamiento personalizado2Modificadores de acceso y encapsulamiento
Visión general de los niveles de accesoMétodos Getter y SetterOcultamiento de informaciónLa palabra clave finalResumen - Gestor de cuentas bancarias5Polimorfismo
Fundamentos de sobrecarga de métodosSobrescritura de métodos (tiempo de ejecución)Upcasting y DowncastingEl operador instanceofClases y métodos abstractosResumen - Calculadora de figuras8Conceptos avanzados de POO
Composición vs. HerenciaAgregación vs. ComposiciónClases internas, anidadas y anónimasEnums y métodos de EnumRecords (Java 16+)Clases selladas (Java 17+)11Patrones de diseño - Parte 1
Introducción a los patrones de diseñoPatrón SingletonPatrón FactoryPatrón BuilderPatrón ObserverPatrón Strategy3Propiedades de clase y miembros estáticos
Variables de instancia vs. estáticasMétodos estáticosBloques estáticosConstantes (static final)Repaso - Contador y utilidad6Interfaces y Clases Abstractas
Introducción a las InterfacesImplementación de InterfacesImplementación de Múltiples InterfacesMétodos Default y Static en InterfacesClases Abstractas vs InterfacesInterfaces FuncionalesResumen - Sistema de Pagos9Genéricos
Introducción a los genéricosClases genéricasMétodos genéricosParámetros de tipo delimitadosComodines (?, extends, super)Resumen - Contenedor genérico12Patrones de diseño Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositePatrón Iterator