Patrón de diseño State - Salud Electrónica

Patrón de diseño State

A la hora de diseñar software hay requerimientos que por su naturaleza tienen un mayor grado de complejidad, ya sea porque se requieren manejar muchas variables, se tienen diversos comportamientos o la lógica de los estados requiere múltiples reglas que se deben cumplir para su correcto funcionamiento. Puede llegar a tomar mucho tiempo idear una solución desde cero que sea mantenible, flexible y escalable, pero gracias a los patrones de diseño se pueden implementar soluciones que son usadas en muchas de las aplicaciones que utilizamos en nuestro día a día. Los patrones de diseño brindan planos predefinidos que pueden ser adaptados a nuestros requerimientos y de acuerdo a las necesidades de los mismos. Existen muchos patrones de diseño e incluso están divididos por categorías, pero por ahora solo vamos a revisar todo lo relacionado con el patrón de diseño State: funcionamiento, cuando usarlo, como implementarlo, los pros y contras.

¿Qué problema soluciona?

Todo programa, módulo o interfaz gráfica normalmente requiere de un estado, que va desde un simple booleano hasta una serie de estados que tienen comportamientos específicos y a su vez interactúan entre sí para definir lo que puede o no puede hacer el programa en determinado estado. Consideremos el siguiente ejemplo: Tenemos que diseñar un programa para controlar completamente una maquina expendedora. También podemos pensar en el comportamiento base de la máquina, en donde tiene las funcionalidades básicas de selección de producto, dispensar y dar el cambio.

Si diseñáramos el ejemplo anterior sin tomar en cuenta el patrón de diseño State, podríamos pensar que nuestro programa tendrá un método llamado “seleccionarProducto()”, pero hay consideraciones a tener en cuenta para que nuestro método funcione bien. ¿Qué pasa si la maquina no tiene monedas para el cambio? Pues agregaríamos una validación para que el método no pueda ser llamado y el usuario no pueda seleccionar un producto. ¿Y qué pasa si hay monedas, pero el producto no tiene stock? Tendríamos que agregar otra validación para controlar esto. Así podríamos estar definiendo todos los posibles comportamientos que pueda tener la maquina al momento de seleccionar el producto y a su vez agregando más y más validaciones dentro del mismo bloque de código, siendo al final muy difícil de mantener, de hacerle algún cambio y dificultando al desarrollador entender todo lo que sucede con el método “seleccionarProducto()”.

El patrón de diseño State busca solucionar este conjunto de estados que en principio están agrupados todos en un mismo método con un montón de validaciones, pero que, si aplicamos el patrón podemos segmentar cada estado en una clase diferente, en donde en cada clase el método “seleccionarProducto()” tendrá las validaciones correspondientes de cada estado.

¿Cómo se usa?

Con el patrón State podemos segmentar nuestros estados en clases diferentes, en donde cada estado contiene toda la lógica de ese estado en particular y a su vez cada estado sabe a qué estado puede pasar y a cuál no. Para el mismo caso del programa de la máquina expendedora, si lo pensamos teniendo en cuenta el patrón State debemos diseñar una clase por cada posible estado de la máquina y clase tendrá los mismos métodos, pero cada uno con su implementación específica.

Retomando las preguntas que nos hicimos anteriormente y teniendo en cuenta que con este nuevo enfoque debemos crear una clase por cada estado, vamos a definir que métodos y que estados tendrá la máquina expendedora. Primero definamos los métodos base que tendrá, para ello se requiere una interfaz en común que usarán todos los estados y que definirá los métodos que todos deben tener:

Adicional a esto debemos definir qué estados tendrá la máquina expendedora:

     

      • Sin monedas.

      • Con monedas.

      • Seleccionando el producto.

      • Dispensando producto. ·

      • Sin stock. ·

      • Devolviendo cambio.

    En general, los estados anteriores son los que posiblemente puede tener una máquina expendedora en cualquier momento y que el programa debe poder controlar para que funcione correctamente. La máquina puede tener más estados, pero para efectos prácticos del ejemplo se resumieron a los más esenciales.

    ¿Cómo implementarlo?

    Ya sabemos cuál es el comportamiento general de los estados y ya definimos cuales son los posibles estados que tendrá la máquina, ahora podemos realizar cada implementación de cada estado y establecer como se va a comportar de acuerdo con la acción que se ejecute y al estado actual de la máquina.

    Lo que primero se define es una clase denominada contexto, esta clase debe implementar la interfaz que definimos anteriormente y adicionalmente es la clase que conoce cual es el estado actual de la maquina y lo guarda para que pueda ser usado en todos los métodos. La clase de contexto luciría de la siguiente manera:

    Como vemos, la clase contexto “MaquinaExpendedora” implementa la interfaz “MaquinaExpendedoraState”. Esta clase contiene el estado actual de la máquina, el total de monedas, los productos que hay disponibles, establece como estado inicial el estado “SinMonedasState” y además cada método llama a su vez al método del estado actual, que es el que define lo que se hará con determinada acción.

    Una vez teniendo la clase contexto, se pueden comenzar a implementar cada uno de los estados con sus respectivas particularidades. Al implementar la interfaz, el estado debe si o si hacer su propia implementación con las validaciones que le correspondan a ese estado y adicionalmente el estado debe poder actualizar a un nuevo estado diferente si es necesario. Para efectos prácticos solo se explicará el estado inicial (SinMonedasState) de la máquina expendedora, pero el resto se implementarían de igual manera.

    Esta clase “SinMonedasState” que representa al estado “Sin monedas”, implementa la interfaz “MaquinaExpendedoraState” y a su vez debe implementar todos los métodos bajo las propias reglas del estado. Para que funcione correctamente el estado debe recibir la instancia del contexto para poder establecer nuevos estados sobre ese contexto y también para obtener o actualizar datos generales de este. La implementación del método “insertarMoneda” se encarga solamente de actualizar las monedas y de verificar si el valor cumple con el mínimo permitido, dependiendo de si es suficiente o no, el estado se actualiza directamente en la referencia al objeto del contexto. Podemos ver que, si tiene monedas suficientes, la maquina entra en el estado “ConMonedasSuficientesState” y ese estado se encargará de controlar los métodos de acuerdo con la lógica establecida para el mismo y lo mismo sucede si no son suficientes monedas la máquina pasa al estado “ConMonedasInsuficientesState”. Adicionalmente vemos que la implementación del método “seleccionarProducto” imprime el mensaje “Por favor, inserte monedas primero”, esto es porque en el este estado, realmente no tiene sentido seleccionar un producto.

    Este fue un ejemplo de un estado, para el resto de los estados se debe hacer lo mismo: Crear una clase que implemente la interfaz “MaquinaExpendedoraState”, debe almacenar la referencia al contexto, debe implementar los métodos que exige la interfaz y debe agregar las validaciones y flujo necesario para los métodos de ese estado en concreto.

    Pros y contras

    Como vimos en el ejemplo dado es que, al separar los estados por clase, es más fácil definir las responsabilidades que tiene cada uno y sus respectivas validaciones para cuando la máquina entre en ese estado en concreto.

    También se tiene un código tolerante a los cambios, ya que cada estado es independiente y no afecta a los demás.

    Facilita el manejo de muchos estados, independiente de la dificultad de cada uno de estos.

    Este patrón es útil solo si la cantidad de estados es grande, pero puede ser excesivo si son pocos estados o que representen muy poca dificultad.

    Referencias

    State. (s. f.). https://refactoring.guru/es/design-patterns/state

    David Vélez

    Soy David, Gerente de Salud Electrónica, mi pasión es ofrecer productos innovadores e integrales que aporten a los procesos en salud para mejorar la eficiencia de las instituciones.

    Formación académica:

    Cuento con la siguiente experiencia laboral:

    • Director médico en instituciones de alta complejidad.
    • Coordinador de servicios hospitalarios y ambulatorios.
    • Docente universitario.

    En mi tiempo libre me gusta cocinar, leer sobre tecnología y actualidad.

    Registra tus datos y uno de nuestros funcionarios se pondrá en contacto contigo

    × ¿Cómo podemos ayudarte?