¿Cuál es el patrón de diseño "Arreglar todo"?

74

En este artículo de 2003 de Stephen Figgins en linuxdevcenter.com , se describe que BitTorrent de Bram Cohen utiliza el patrón de diseño "Arreglar todo".

Un enfoque menos común que hace que BitTorrent sea más difícil de entender, pero digno de estudio, es el uso de la idempotencia por parte de Cohen. Un proceso es idempotente cuando aplicarlo más de una vez no causa más cambios. Cohen dice que usa un patrón de diseño que llama "Arreglar todo", una función que puede reaccionar a una serie de cambios sin notar realmente todo lo que podría cambiar. Él explica, "usted nota el evento que sucedió, luego llama a la función arreglar todo que está escrita de esta manera tan idempotente, y simplemente limpia lo que pueda estar pasando y lo recalcula todo desde cero". Si bien la idempotencia facilita algunos cálculos difíciles, hace que las cosas sean un poco complicadas. No siempre está claro qué va a cambiar una llamada, en todo caso. No necesita saber de antemano. Eres libre de llamar a la función,

Esto suena bastante bien a primera vista.

Sin embargo, me parece que llamar a una función idempotente de "arreglar todo" mejoraría la robustez del sistema a costa de la eficiencia y potencialmente arruinaría el sistema contenedor (que podría preferir procesos que planifiquen y ejecuten cuidadosamente).

Sin embargo, no puedo decir que lo haya usado antes. Tampoco puedo encontrar la fuente de su solicitud en línea (pero encontré esta que dice estar basada en ella). Tampoco puedo encontrar referencias a él fuera de este artículo (y considero que mi google-fu es bastante bueno) pero encontré una entrada para "Capacidad Idempotente" en SOApatterns.org .

¿Es esta idea mejor conocida por otro nombre?

¿Qué es el patrón de diseño "Arreglar todo"? ¿Cuáles son sus pros y sus contras?

Aaron Hall
fuente
44
Sospecho que el nombre también es una referencia a la idea del punto fijo de una función, x = f (x). No importa cuántas veces aplique f a x , el resultado es el mismo. Una vez que haya logrado el resultado correcto, volver a procesar el resultado correcto devuelve el mismo resultado correcto.
9000
77
Tenga en cuenta que cualquiera puede dar cualquier nombre a lo que quiera, pero eso no lo convierte en un patrón de software bien conocido. La idempotencia es un concepto bien conocido por derecho propio; solo parece que se está usando creativamente aquí.
Robert Harvey
1
Esto me recuerda cómo se implementó el Main Event Loop en Mac OS. Era una función única que respondía a cualquier evento y generalmente estaba estructurada para probar el estado de todos los controles y actualizar toda la interfaz de usuario según fuera necesario. Idempotente, de hecho.
Lucas
3
This sounds quite nice on the face of it. De Verdad? ¡Suena horrible para mí!
Michael
3
@Michael ¿No te gustan los gestores de paquetes? Funcionan según el mismo concepto, solo que a menor escala: marque cómo quiere que se vea el sistema, ejecute "arreglar todo", instala / elimina / actualiza según corresponda, pero solo tiene algo que ver si hubo cambios.
Izkata

Respuestas:

100

Supongamos que tiene una página HTML que es bastante complicada: si elige algo en un menú desplegable, puede aparecer otro control o los valores en un tercer control pueden cambiar. Hay dos formas de abordar esto:

  1. Escriba un controlador separado, para cada control, que responda a eventos en ese control y actualice otros controles según sea necesario.

  2. Escriba un solo controlador que observe el estado de todos los controles en la página y simplemente arregle todo .

La segunda llamada es "idempotente" porque puede llamarla una y otra vez y los controles siempre se organizarán correctamente. Mientras que la (s) primera (s) llamada (s) pueden tener problemas si una llamada se pierde o se repite, por ejemplo, si uno de los manejadores realiza un cambio.

La lógica para la segunda llamada sería un poco más oscura, pero solo tiene que escribir un controlador.

Y siempre puede usar ambas soluciones, llamando a la función "arreglar todo" según sea necesario "solo para estar seguro".

El segundo enfoque es especialmente bueno cuando el estado puede provenir de diferentes fuentes, por ejemplo, de la entrada del usuario frente a la del servidor. En ASP.NET, la técnica juega muy bien con el concepto de devolución de datos porque solo ejecuta la función arreglar todo cada vez que renderiza la página.

Ahora que mencioné que los eventos se perdieron o se repitieron y obtuvieron estados de diferentes fuentes, creo que es obvio cómo este enfoque se asigna bien a un espacio problemático como el de BitTorrent.

¿Contras? Bueno, la desventaja obvia es que hay un impacto en el rendimiento porque es menos eficiente revisar todo todo el tiempo. Pero una solución como BitTorrent está optimizada para escalar, no escalar, por lo que es bueno para ese tipo de cosas. Dependiendo del problema que intente resolver, puede que no sea adecuado para usted.

John Wu
fuente
9
Me parece que MVC es típico de "Arreglar todo": cuando modifica el modelo y luego vuelve a dibujar la vista desde cero, la vista se vuelve a dibujar completamente, sin intentar adivinar qué partes podría haber afectado la acción.
Matthieu M.
3
Esto suena esencialmente como el principio detrás de sistemas como Saltstack, Ansible y Nix. Dada una descripción de una configuración, teóricamente puede llevar varios sistemas diversos al mismo estado final.
kojiro
1
@MatthieuM. Reaccionar , que es muy popular en el desarrollo frontend, es así excepto que dom virtuales diffing por lo que sólo actualiza el DOM real con los cambios reales
Izkata
2
@Izkata Incluso más que Reaccionar, esta respuesta me hizo pensar en Redux.
Kevin
2
Puede ser útil señalar que "arreglar todo" e idempotente son cosas diferentes: "arreglar todo" generalmente es idempotente (pero no tiene que serlo), y las operaciones idempotentes no necesitan arreglar todo o incluso tener un desempeño penalización: solo da el mismo resultado cuando se ejecuta dos veces.
Hans-Peter Störr
15

Creo que el artículo está un poco anticuado porque, al leerlo, no es una idea poco ortodoxa o nueva. Esta idea se presenta como un patrón separado cuando realmente es solo una implementación simple de Observer. Pensando en lo que estaba haciendo en ese momento, recuerdo trabajar en la lógica para sentarme detrás de una interfaz algo compleja con varios paneles diferentes con datos que eran interdependientes. El usuario podría cambiar los valores y / o ejecutar una rutina de optimización y, en función de esas acciones, se generaron eventos que la interfaz de usuario escucharía y actualizaría según fuera necesario. Hubo una serie de problemas durante el desarrollo en los que ciertos paneles no se actualizaron cuando deberían. La solución (permanecer dentro del diseño) fue generar eventos a partir de otros eventos. Finalmente, para cuando todo funcionaba bien, Casi todos los cambios dieron como resultado la actualización de todos los paneles. Toda la complejidad de tratar de aislar cuando un panel determinado necesitaba actualizarse fue en vano. Y no importó de todos modos. Fue efectivamente una optimización prematura. Hubiera ahorrado un montón de tiempo y esfuerzo simplemente colapsando todo en un solo evento que refrescó todo.

Hay innumerables sistemas diseñados en el "arreglar todo" o actualizar todo. Piense en todas las interfaces CRUD que agregan / actualizan una fila y luego solicitan la base de datos. Este no es un enfoque exótico, es solo la obvia solución no inteligente. Tienes que darte cuenta de que en 2003, era el colmo de la "fiebre del patrón". Por lo que pude ver, la gente pensó que nombrar nuevos patrones sería su camino hacia la fama y la riqueza. No me malinterpreten, creo que el concepto de un patrón es extremadamente útil para describir soluciones en abstracto. Las cosas se salieron un poco de los rieles. Es lamentable porque creó mucho cinismo sobre el concepto de patrón en general. Es solo en este contexto que tiene sentido hablar de esto como una solución 'poco ortodoxa'. Eso' s similar a la ortodoxia alrededor de los ORM o contenedores DI. No usarlos se considera poco ortodoxo a pesar de que las personas habían estado construyendo software mucho antes de que existieran estas herramientas y, en muchos casos, esas herramientas son excesivas.

Así que volvamos a 'arreglar todo'. Un ejemplo simple es calcular medios. La solución simple es sumar números y dividirlos por la cardinalidad de los valores. Si agrega o modifica un número, simplemente vuelva a hacerlo, desde el principio. Puede realizar un seguimiento de la suma y el recuento de números y cuando alguien agrega un número, aumenta el recuento y lo agrega a la suma. Ahora no está volviendo a agregar todos los números nuevamente. Si alguna vez trabajó con Excel con una fórmula que hace referencia a un rango y modificó un solo valor en ese rango, tiene un ejemplo del patrón 'arreglar todo', es decir, cualquier fórmula que tenga una referencia a ese rango volverá a calcular independientemente de si ese valor era relevante (por ejemplo, usando algo como sumif ()).

Esto no quiere decir que no sea una elección inteligente en un contexto dado. En el ejemplo, digamos que ahora necesitamos admitir actualizaciones. Ahora necesito saber el valor anterior de alguna manera y solo cambiar la suma por el delta. Nada de esto es realmente tan desafiante hasta que considere intentar hacer esto en un entorno distribuido o concurrente. Ahora tiene que manejar todo tipo de problemas de tiempo espinoso y probablemente terminará creando un cuello de botella importante que ralentiza las cosas mucho más que volver a calcular.

El resultado aquí es que es mucho más fácil acertar con el enfoque de "arreglar todo" o "actualizar todo". Puede hacer que un enfoque más sofisticado funcione, pero es mucho más complicado y, por lo tanto, es más probable que tenga fallas. Además, en muchos contextos, el enfoque de "actualizar todo" puede ser más eficiente. Por ejemplo, los enfoques de copia en escritura son generalmente más lentos para los enfoques de subproceso único, pero cuando tiene una alta concurrencia, puede permitirle evitar bloqueos y, por lo tanto, proporcionar un mejor rendimiento. En otros casos, puede permitirle agrupar cambios de manera eficiente. Entonces, para la mayoría de los problemas, es probable que desee comenzar con un enfoque de actualización de todo, a menos que tenga una razón específica por la que no puede hacerlo y luego se preocupe por hacer algo más complejo una vez que lo necesite.

JimmyJames
fuente
2
Estoy bastante seguro de que Excel tiene la intención de recalcular solo las celdas que dependen de los cambios, por lo que hay una manera de activar el recálculo de todas las celdas: superuser.com/questions/448376/… (lo que supongo sería un "arreglar todo" )
Aaron Hall el
@AaronHall Si lo hace, es una implementación realmente mala. Regularmente veo que consume el 100% de 7 CPU durante 15-30 minutos para calcular, por ejemplo, 60,000 células. Los cálculos no son complicados. A menudo he escrito programas de Python que pueden hacer todo en la hoja en unos segundos, incluido el inicio de Python. Esta fue mi mejor suposición sobre cómo podría llevar tanto tiempo. Podría ser otra cosa, supongo. También hay una serie de errores realmente antiguos en Excel que podrían ser la razón de esa característica.
JimmyJames
1
@AaronHall también es posible con ese usuario que el cálculo automático esté desactivado en la hoja. A menudo hago esto en libros grandes porque no tengo 15 minutos de sobra cada vez que presiono enter.
JimmyJames
@ AaronHall Pensé un poco más y tienes razón. Mis suposiciones probablemente fueron demasiado amplias. Actualicé la respuesta más centrada en algo en lo que tengo más confianza.
JimmyJames
2
@JimmyJames: Lo que quería decir es que el mejor enfoque puede variar mucho según las circunstancias, y "arreglar todo" se puede subdividir en "arreglar todo con entusiasmo en cada cambio individual" y "arreglar todo perezosamente después de que todos los cambios estén completos ".
supercat
4

No estoy seguro de que sea un "patrón de diseño", pero clasificaría ese tipo de comportamiento como configuración de estado final o configuración de estado deseada , en la línea de Puppet, Chef o Powershell DSC.

Esas soluciones generalmente operan a nivel de administración de sistemas, no a nivel de lógica de negocios como se describe en la pregunta, pero es efectivamente el mismo paradigma, y ​​aunque tales herramientas son generalmente de naturaleza declarativa, los mismos principios se pueden aplicar en código de procedimiento o secuencias de comandos.

Dan1701
fuente
1

Lo he usado principalmente en las interfaces de usuario. Lo bueno es que lo escribe una vez, y maneja todo, desde el caso más simple hasta el más difícil, igualmente bien (por ejemplo, si el usuario gira la pantalla, o en una computadora portátil / escritorio si el usuario cambia el tamaño de una ventana, y prácticamente todo cambia )

No hay muchas razones para preocuparse por la eficiencia. En la interfaz de usuario, las cosas caras son cosas como volver a dibujar un elemento que se ha movido. El cálculo de dónde va cada elemento y qué tan grande es generalmente es bastante rápido. Todo lo que debe asegurarse es que cada vez que encuentre que un elemento debe permanecer exactamente en el lugar donde pertenece, no se ejecuta ningún código para moverlo. Los cambios reales son todas las cosas que tenía que hacer de todos modos.

gnasher729
fuente
0

Suena como principios de programación reactiva. "Arreglar todo" examina el estado "núcleo" actual y propaga todo lo demás que debería verse afectado: "estados calculados". Si optimiza esta derivación, puede alcanzar una alta eficiencia, a-la React, si se hace ingenuamente, el rendimiento puede no ser óptimo, aunque podría ser lo suficientemente rápido.

orip
fuente