Un patrón de diseño es una plantilla que resuelve un problema recurrente en el diseño de software.

El patrón de estado es un patrón de comportamiento que permite que un objeto altere su comportamiento cuando cambia su estado interno.

Aquí aprenderá a usar el patrón de estado en TypeScript.

¿Qué es el patrón de estado?

El patrón de diseño de estados está estrechamente relacionado con una máquina de estados finitos, que describe un programa que existe en un finito número de estados en un momento dado y se comporta de manera diferente dentro de cada estado.

Existen reglas limitadas y predeterminadas, transiciones, que rigen los otros estados a los que cada estado puede cambiar.

Por contexto, en una tienda en línea, si el pedido de compra de un cliente ha sido “entregado”, no puede ser “cancelado” porque ya ha sido “entregado”. "Entregado" y "Cancelado" son estados finitos del pedido, y el pedido se comportará de manera diferente según su estado.

El patrón de estado crea una clase para cada estado posible, con un comportamiento específico del estado contenido en cada clase.

instagram viewer

Un ejemplo de aplicación basada en el estado

Por ejemplo, suponga que está creando una aplicación que rastrea los estados de un artículo para una editorial. Un artículo puede estar pendiente de aprobación, redactado por un escritor, editado por un editor o publicado. Estos son los estados finitos de un artículo a publicar; dentro de cada estado único, el artículo se comporta de manera diferente.

Puede visualizar los diferentes estados y transiciones de la aplicación del artículo con el siguiente diagrama de estado:

Al implementar este escenario en el código, primero debe declarar una interfaz para el artículo:

interfazArtículoInterfaz{
paso(): vacío;
borrador(): vacío;
editar(): vacío;
publicar(): vacío;
}

Esta interfaz tendrá todos los estados posibles de la aplicación.

A continuación, cree una aplicación que implemente todos los métodos de la interfaz:

// Solicitud
claseArtículoimplementosArtículoInterfaz{
constructor() {
este.mostrarEstadoActual();
}

privadoMostrar estado actual(): vacío{
//...
}

públicopaso(): vacío{
//...
}

públicoborrador(): vacío{
//...
}

públicoeditar(): vacío{
//...
}

públicopublicar(): vacío{
//...
}
}

Lo privado Mostrar estado actual El método es un método de utilidad. Este tutorial lo usa para mostrar lo que sucede en cada estado. No es una parte requerida del patrón de estado.

Manejo de transiciones de estado

A continuación, deberá manejar las transiciones de estado. Manejar la transición de estado en su clase de aplicación requeriría muchas declaraciones condicionales. Esto daría como resultado un código repetitivo que es más difícil de leer y mantener. Para resolver este problema, puede delegar la lógica de transición de cada estado a su propia clase.

Antes de escribir cada clase de estado, debe crear una clase base abstracta para asegurarse de que cualquier método llamado en un estado no válido arroje un error.

Por ejemplo:

abstractoclaseArtículoEstadoimplementosArtículoInterfaz{
tono (): estado del artículo {
tirarnuevoError("Operación no válida: no se puede realizar la tarea en estado actual");
}

borrador (): Estado del artículo {
tirarnuevoError("Operación no válida: no se puede realizar la tarea en estado actual");
}

editar (): Estado del artículo {
tirarnuevoError("Operación no válida: no se puede realizar la tarea en estado actual");
}

publicar (): Estado del artículo {
tirarnuevoError("Operación no válida: no se puede realizar la tarea en estado actual");
}
}

En la clase base anterior, cada método arroja un error. Ahora, debe anular cada método creando clases específicas que extiende la clase base para cada estado. Cada clase específica contendrá una lógica específica del estado.

Cada aplicación tiene un estado inactivo, que inicializa la aplicación. El estado inactivo de esta aplicación establecerá la aplicación en el borrador estado.

Por ejemplo:

clasePendingDraftStateextiendeArtículoEstado{
tono (): estado del artículo {
devolvernuevo EstadoBorrador();
}
}

El paso El método de la clase anterior inicializa la aplicación estableciendo el estado actual en DraftState.

A continuación, anule el resto de los métodos así:

claseDraftStateextiendeArtículoEstado{
borrador (): Estado del artículo {
devolvernuevo EditingState();
}
}

Este código anula el borrador método y devuelve una instancia del Estado de edición.

claseEstado de ediciónextiendeArtículoEstado{
editar (): Estado del artículo {
devolvernuevo EstadoPublicado();
}
}

El bloque de código anterior anula el editar método y devuelve una instancia de EstadoPublicado.

claseEstadoPublicadoextiendeArtículoEstado{
publicar (): Estado del artículo {
devolvernuevo EstadoBorradorPendiente();
}
}

El bloque de código anterior anula el publicar método y vuelve a poner la aplicación en su estado inactivo, PendingDraftState.

Luego, debe permitir que la aplicación cambie su estado internamente haciendo referencia al estado actual a través de una variable privada. Puede hacer esto inicializando el estado inactivo dentro de su clase de aplicación y almacenando el valor en una variable privada:

privado estado: EstadoArtículo = nuevo EstadoBorradorPendiente();

A continuación, actualice la Mostrar estado actual método para imprimir el valor del estado actual:

privadoMostrar estado actual(): vacío{
consola.registro(este.estado);
}

El Mostrar estado actual El método registra el estado actual de la aplicación en la consola.

Finalmente, reasigne la variable privada a la instancia de estado actual en cada uno de los métodos de su aplicación.

Por ejemplo, actualice sus aplicaciones paso al siguiente bloque de código:

públicopaso(): vacío{
este.estado = este.estado.tono();
este.mostrarEstadoActual();
}

En el bloque de código anterior, el paso El método cambia el estado del estado actual al estado de tono.

De manera similar, todos los demás métodos cambiarán el estado del estado actual de la aplicación a sus respectivos estados.

Actualice sus métodos de aplicación a los bloques de código a continuación:

El borrador método:

públicoborrador(): vacío{
este.estado = este.estado.borrador();
este.mostrarEstadoActual();
}

El editar método:

públicoeditar(): vacío{
este.estado = este.estado.edit();
este.mostrarEstadoActual();
}

Y el publicar método:

públicopublicar(): vacío{
este.estado = este.estado.publicar();
este.mostrarEstadoActual();
}

Uso de la aplicación finalizada

Su clase de aplicación terminada debe ser similar al bloque de código a continuación:

// Solicitud
claseArtículoimplementosArtículoInterfaz{
privado estado: EstadoArtículo = nuevo EstadoBorradorPendiente();

constructor() {
este.mostrarEstadoActual();
}

privadoMostrar estado actual(): vacío{
consola.registro(este.estado);
}

públicopaso(): vacío{
este.estado = este.estado.tono();
este.mostrarEstadoActual();
}

públicoborrador(): vacío{
este.estado = este.estado.borrador();
este.mostrarEstadoActual();
}

públicoeditar(): vacío{
este.estado = este.estado.edit();
este.mostrarEstadoActual();
}

públicopublicar(): vacío{
este.estado = este.estado.publicar();
este.mostrarEstadoActual();
}
}

Puede probar las transiciones de estado llamando a los métodos en la secuencia correcta. Por ejemplo:

constante documentos = nuevo Artículo(); // EstadoBorradorPendiente: {}

docs.tono(); // EstadoBorrador: {}
docs.draft(); // Estado de edición: {}
docs.edit(); // EstadoPublicado: {}
docs.publish(); // EstadoBorradorPendiente: {}

El bloque de código anterior funciona porque los estados de la aplicación cambiaron de forma adecuada.

Si intenta cambiar el estado de una manera que no está permitida, por ejemplo, del estado de tono al estado de edición, la aplicación arrojará un error:

constante documentos = nuevo Artículo(); // EstadoBorradorPendiente: {}
docs.tono() // EstadoBorrador: {}
docs.edit() // Operación no válida: no se puede realizar la tarea en el estado actual

Solo debes usar este patrón cuando:

  • Está creando un objeto que se comporta de manera diferente dependiendo de su estado actual.
  • El objeto tiene muchos estados.
  • El comportamiento específico del estado cambia con frecuencia.

Ventajas y compensaciones del patrón de estado

Este patrón elimina declaraciones condicionales voluminosas y mantiene los principios de responsabilidad única y abierto/cerrado. Pero puede ser excesivo si la aplicación tiene pocos estados o sus estados no son particularmente dinámicos.