¡Escribamos un ensayo ! Pero esa es una tarea imposible. Sin embargo, podemos crear un proceso que, si se ejecutara eternamente, generaría un ensayo infinitamente largo. . infinitamente largo Suficientemente cerca Ahora, obviamente puedes generar un ensayo largo y repetitivo con una sola línea de código Python: >>> "This is water. " * 20 'This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. This is water. ' … ¡abucheo! En cambio, generaremos un ensayo mucho más interesante utilizando el patrón de diseño Estado en este artículo. Bostezo Primero, entenderemos qué son las máquinas de estados y cómo se relacionan con el patrón de diseño de estados. A continuación, crearemos una máquina de estados que pueda generar un ensayo infinito (razonablemente) interesante. Luego, repasaremos brevemente el patrón de diseño estatal. Finalmente, traduciremos esa máquina de estados a código orientado a objetos utilizando el patrón de diseño de estados. Los patrones de diseño de software son formas efectivas de resolver problemas que ocurren comúnmente. Cuando se aplican adecuadamente, los patrones de diseño de software, como el patrón de estado, pueden ayudarlo a escribir un software mejor escalable, mantenible y comprobable. Máquina estatal En esencia, el patrón de diseño State traduce una máquina de estados en código orientado a objetos. Si no está familiarizado con las máquinas de estados, es un concepto bastante simple. Una máquina de estados tiene y . Los estados son ciertas propiedades de nuestro sistema de interés, y las transiciones de estado son acciones que cambian estas propiedades y, por lo tanto, también causan un cambio de estado. estados transiciones Como tengo experiencia en robótica (entre otras cosas) y debido a que las máquinas de estados se usan ampliamente en robótica, usaré un ejemplo simple de un robot aspirador para ilustrar cómo funcionan las máquinas de estados. El diagrama de la máquina de estados muestra una imagen intuitiva de cómo opera el robot, incluso si nunca se ha encontrado con máquinas de estados. Repasemos esta operación paso a paso. El robot se inicia en el estado (el punto negro indica el estado inicial). Atracado Si el robot detecta que su batería está baja, comienza a cargarse solo (estado ) hasta que la batería esté llena. Una vez que la batería está llena, vuelve al estado . de carga acoplado En el estado , si el robot detecta que el piso está sucio (y su batería no está baja), comienza a limpiar el piso (estado ). Acoplado de limpieza En el estado , si el robot tiene poca batería, se cargará solo. Y si el suelo está limpio, el robot vuelve a su base (estado ). de limpieza Docked Así, nuestro robot aspirador tiene tres estados ( , y ) y tiene transiciones basadas en la detección sensorial del suelo y su batería. Atracado Limpiando Cargando Máquina de estados simple para ensayo infinito. Ahora que entendemos las máquinas de estados en un nivel básico, creemos una máquina de estados capaz de escribir un ensayo infinito. Arriba hay una máquina de estados que utiliza la gramática inglesa para generar un ensayo compuesto por oraciones cortas y simples. Prometo que llegaremos a una versión más interesante muy pronto, pero esto debería servir como un buen punto de partida para la comprensión. Repasemos cómo funciona. Comenzando en el estado , generamos un sustantivo seleccionándolo de una lista predefinida de sustantivos. Digamos que nuestro sustantivo es “El Mundo”. (frase hasta el momento: “El Mundo”) Sustantivo Luego terminamos en el estado , generando un verbo a continuación (digamos, “ladra”). (frase hasta el momento: “El mundo ladra”) Verbo Generamos un adjetivo (digamos, "rojo") en el estado . (frase hasta el momento: “El mundo ladra rojo”) Adjetivo Luego, en el estado , generamos uno de los signos de puntuación finales, digamos "!". (frase: “¡El mundo ladra rojo!”) Endmark Finalmente, volvemos al estado para generar nuestra siguiente oración en el ensayo. Sustantivo Esta máquina de estados podría generar un ensayo (sin sentido) similar a este. ¡El mundo ladra rojo! ¿El primo Harry llueve mal? Los tigres brillan de forma divertida. … Máquina de estados no determinista para ensayo infinito Aunque “no determinista” suena complicado, para nuestros propósitos simplemente significa agregar algo de aleatoriedad. Básicamente, estamos agregando una especie de lanzamiento de moneda antes de hacer la transición a algunos de los estados. Verás lo que quiero decir. La máquina de estados no determinista anterior es muy similar a la anterior. Las únicas diferencias son: Las negaciones son palabras como "no" o "no" y las conjunciones son palabras como "y" y "pero". En el estado , generamos un verbo y luego lanzamos una moneda. Si sale cara (50% de probabilidad), pasamos al estado ; de lo contrario pasamos al estado . Verbo de Negación Adjetivo De manera similar, en el estado , generamos un adjetivo y luego lanzamos una moneda. Si sale cara, pasamos al estado ; si es cruz, entonces pasamos al estado . Adjetivo de Conjunción Endmark Con la introducción de la aleatoriedad, la negación y las conjunciones, ahora podemos generar oraciones más interesantes y de longitud variable. Patrón de diseño estatal Ahora, comprendamos cómo funciona el patrón de diseño estatal. Nuevamente, recuerde que estamos intentando traducir una máquina de estados a código orientado a objetos. En la máquina de estados de generación de ensayos, observe que cada estado necesita hacer dos cosas. Realizar alguna acción. En este caso, generando una palabra (sustantivo, adjetivo, etc.). Transición al siguiente estado. Del al , etcétera. sustantivo verbo Desde el punto de vista de un Estado en particular, no hay que deba saber o hacer. En lugar de quedar estancados por la complejidad de todo el sistema (todos sus estados y transiciones), podemos centrarnos simplemente en un estado a la vez. En mi opinión, este tipo de y es el mayor atractivo del modelo estatal. nada más aislamiento desacoplamiento A continuación, tenemos un diagrama para el patrón de diseño State. Hay algún contexto en el que opera cada uno de los estados, ilustrado por la clase . El objeto de contexto tiene un atributo de estado privado, que utiliza para llamar al estado actual para realizar su acción. Cada estado implementa una interfaz con métodos para realizar su acción u operación y devolver el siguiente estado. UML Context State Si asignamos esto al ejemplo de generación de ensayos, el diagrama UML se verá así. es ahora una clase abstracta (indicada en cursiva) en lugar de una interfaz. Las clases abstractas pueden tener algunos métodos y atributos abstractos (no implementados), mientras que otros pueden definirse. Las interfaces son totalmente abstractas: todos sus métodos son abstractos. Hice este cambio porque la implementación es la misma en todos los estados y es bueno evitar el código duplicado. WordState generateWord Analicemos cada uno de los atributos y métodos anteriores. En la clase , tenemos: EssayContext : Referencia al objeto actual. state WordState : Lista de todas las palabras generadas hasta el momento. essayBody : Configurador para cambiar el atributo . setState() state : método para agregar la siguiente palabra al cuerpo del ensayo. addWord() : llamamos a este método para generar nuestro ensayo; Nos detenemos cuando el tiene una longitud mayor que . generateEssay() essayBody length : Devuelve una cadena del ensayo generado. printEssay() En la clase abstracta , tenemos: WordState : Propiedad abstracta (indicada en cursiva) para una lista de palabras entre las cuales elegimos las palabras para generar. wordList : método que agrega la palabra generada al contexto del ensayo. generateWord() : método abstracto para devolver el siguiente estado. nextState() Usaremos como ejemplo representativo de todos los demás estados concretos heredados de . NounState WordState : una lista de sustantivos de la cual elegimos palabras para generar. wordList : Devuelve el siguiente estado. nextState() Ahora tenemos todo lo que necesitamos para implementar esto en el código. ¡Sigamos adelante y hagamos justamente eso! Código Python Primero escribamos la clase en un archivo llamado . Nos desharemos del caso del camello y cambiaremos al caso de la serpiente porque, bueno, Python es una... serpiente (lo siento). EssayContext essay_context.py from word_state import WordState class EssayContext: def __init__(self, state: WordState): self.state = state self.essay_body: list[str] = [] def set_state(self, state: WordState): self.state = state def add_word(self, word: str): self.essay_body.append(word) def generate_essay(self, length: int): while len(self.essay_body) < length: self.state.generate_word(self) def print_essay(self) -> str: return " ".join(self.essay_body) Luego, agreguemos los estados en un archivo llamado . word_state.py import abc import numpy as np class WordState(abc.ABC): word_list: list[str] @classmethod def generate_word(cls, context: "EssayContext"): word = np.random.choice(cls.word_list) context.add_word(word) context.set_state(cls.next_state()) @classmethod @abc.abstractmethod def next_state(cls) -> "WordState": pass class NounState(WordState): word_list: list[str] = ["everything", "nothing"] @classmethod def next_state(cls): return VerbState class VerbState(WordState): word_list: list[str] = ["is", "was", "will be"] @classmethod def next_state(cls): heads = np.random.rand() < 0.5 if heads: return NegationState return AdjectiveState class NegationState(WordState): word_list: list[str] = ["not"] @classmethod def next_state(cls): return AdjectiveState class AdjectiveState(WordState): word_list: list[str] = ["fantastic", "terrible"] @classmethod def next_state(cls): heads = np.random.rand() < 0.5 if heads: return ConjunctionState return EndmarkState class ConjunctionState(WordState): word_list: list[str] = ["and", "but"] @classmethod def next_state(cls): return NounState class EndmarkState(WordState): word_list: list[str] = [".", "!"] @classmethod def next_state(cls): return NounState Finalmente, agreguemos código para ejecutar todo en main.py from essay_context import EssayContext from word_state import NounState if __name__ == '__main__': ctx = EssayContext(NounState) ctx.generate_essay(100) print(ctx.print_essay()) La ejecución nos da el siguiente resultado (diferente cada vez debido al no determinismo): python main.py 'everything is not terrible and nothing was terrible ! everything will be not fantastic but everything is fantastic . everything will be fantastic . nothing will be fantastic and nothing will be terrible ! everything was not fantastic and everything will be not terrible . everything was terrible . nothing was terrible but nothing will be fantastic ! nothing is not terrible . nothing was not fantastic but everything was not fantastic ! everything will be not fantastic but everything will be terrible ! everything will be not fantastic . everything is fantastic but nothing will be not terrible ! everything will be not fantastic but nothing was not fantastic !' ¡Nada mal para un sistema tan simple! También podemos ampliar las distintas listas de palabras o agregar más estados para hacer que la generación del ensayo sea más sofisticada. Incluso podríamos presentar algunas API de LLM para llevar nuestros ensayos al siguiente nivel. Pensamientos finales Las máquinas de estados y el patrón de estado son ideales para modelar y crear sistemas con una noción bien definida de "estado". Es decir, existen comportamientos y propiedades específicas asociadas a cada estado. El robot aspirador está limpiando, acoplado o cargándose. Su televisor puede estar encendido o apagado, y los botones del control remoto del televisor actuarán de manera diferente según el estado del televisor. También es una buena opción para generar o identificar secuencias con un patrón bien definido. Esto se aplica a nuestro ejemplo de generación de ensayos. Finalmente, podrías preguntarte: “¿Cuál es el sentido de todo esto?” ¿Por qué nos tomamos la molestia de definir los distintos estados y clases para generar este ensayo “infinito”? Podríamos haber escrito 20 (o menos) líneas de código Python para lograr el mismo comportamiento. La respuesta corta es para una mejor . escalabilidad Imagínese si, en lugar de sólo tres o cinco estados, tuviéramos 50 o 500 estados. Esto no es una hipérbole; Los sistemas empresariales reales tienen ese nivel de complejidad. De repente, el patrón del Estado parece mucho más atractivo debido a su desacoplamiento y aislamiento. Podemos concentrarnos en un estado a la vez sin tener que mantener todo el sistema en nuestras cabezas. Es más fácil introducir cambios ya que un estado no afectará a los demás. También permite realizar pruebas unitarias más sencillas, una gran parte de un sistema escalable y mantenible. En última instancia, el patrón estatal no se trata sólo de gestionar estados; Como todos los patrones de diseño, es un modelo para construir sistemas que sean tan escalables y mantenibles como sofisticados.