Término (¿o "patrón"?) Para "Hacer algo si aún no está hecho" [cerrado]

54

Suena bastante básico, lo sé, pero recientemente tuve un colega que me dijo que un método llamado startHttpServeres demasiado complicado de entender porque solo inicia el servidor si aún no se está ejecutando. Encuentro que me meto en problemas cuando respondo: "¿En serio? He estado haciendo esto durante décadas, es un patrón común en la programación". Más a menudo de lo que me importa admitir que regresa con alguna evidencia documentada que muestra que toda la comunidad de programación está detrás de su punto de vista y termino sintiéndome avergonzado.

Pregunta : ¿Existe un patrón de diseño documentado detrás del concepto de un método que no funciona si la acción requerida ya está vigente? O, si no es un patrón, ¿tiene un nombre? Y si no, ¿hay alguna razón para pensar que es demasiado complicado considerar escribir un método de esta manera?

John Calcote
fuente
66
sonidos como el almacenamiento en caché - y que a menudo se considera complicado de hecho (véase también ¿Cómo explico $ {algo} a $ {} a alguien? )
mosquito
8
Obtuviste 3 respuestas diferentes, pero la mejor respuesta sería aplicarlas todas: cambiar el nombre de la función original, dividir su código en dos funciones (donde una de las dos ahora recibe el nombre startHttpServer), y sí, el término "idempotente" se aplica aquí bien.
Doc Brown
8
¿Qué tipo de evidencia contraria proporciona su colega? ¿Me puede proporcionar un enlace?
Robert Harvey
55
Tengo curiosidad por ver de dónde proviene su colega sus citas. Al menos en torno a Stack Overflow e Ingeniería de software, esta función estaría mal nombrada, pero no tendría un comportamiento inusual. Tenga en cuenta que no es porque alguien en algún lugar haya puesto algo en un blog que represente la visión completa de la comunidad de programación. Incluso grandes nombres como Martin Fowler dicen cosas muy extrañas de vez en cuando. Todos somos solo humanos.
T. Sar - Restablece a Mónica el
44
Creo que una mejor manera de pensar en "Hacer algo si aún no está hecho" sería "Poner el sistema en este estado". Por supuesto, si el sistema ya está en ese estado, el método no haría nada, que sería el comportamiento esperado.
T. Sar - Restablece a Mónica el

Respuestas:

127

Como NickWilliams ya ha dicho : el concepto que describe el OP se llama idempotente (sustantivo Idempotency ). De hecho, es una práctica común, especialmente en las API de alto nivel.

PERO: Cambiar el nombre de la función.

En lugar de startHttpServerllamarlo makeSureHttpServerIsRunningo ensureHttpServerIsRunning.

Cuando se llama a una función startHttpServer, los lectores esperan que inicie un servidor HTTP; cuando me llamen diez veces seguidas, tendré diez servidores ejecutándose. Su función no hace eso la mayor parte del tiempo. Además, el nombre con "inicio" sugiere que si quiero que solo se ejecute un servidor, tendré que hacer un seguimiento de si la función ya se ha llamado o no.

Cuando se llama a una función makeSureHttpServerIsRunning, supongo que hará lo necesario para asegurarse de que se está ejecutando un servidor HTTP, lo más probable es que verifique si ya se está ejecutando y lo inicie de otra manera. También supongo que la función se asegura de que el servidor se esté ejecutando realmente (iniciar un servidor puede implicar un tiempo en el que aún no se está ejecutando).

gnasher729
fuente
83
Esta. Personalmente uso "Asegurar" en su lugar. El punto es que esto debería convertirse en un esquema de nombres que se entienda en su equipo.
Eufórico
3
o ha comenzado a lanzar una excepción si ya ha comenzado. como sqlconnection.open
Ewan
99
Siempre uso el nombre "asegurar" para la función "hacer si aún no".
Kaz
3
Otro nombre que a menudo veo es getOrCreateSomething, que solo crea en la primera llamada y luego simplemente devuelve algo
Fabich
55
@aroth ¿Estás seguro de que no esperarías que intente encontrar algún puerto disponible y devolverlo? ¿O simplemente estar mal escrito? ;)
jpmc26
33

Renombrarlo a EnsureServerRunning.

Completamente inequívoco y está claro que se asegura de que se esté ejecutando (si no lo está) sin implicar un reinicio si lo está.

(Alternativa StartServerIfNotRunning:?)

Stilez
fuente
1
La mayoría de las personas que llaman solo se preocupan de que el servidor se esté ejecutando después de la llamada. Cómo se logra eso, no les importa.
gnasher729
29

No es realmente un patrón de diseño, pero llamaría idemotente a su método . Su término generalmente se usa para referirse a llamadas remotas, pero la descripción parece coincidir con lo que está haciendo.

Métodos idempotentes. Los métodos también pueden tener la propiedad de "idempotencia" en que (aparte de los problemas de error o caducidad) los efectos secundarios de N> 0 solicitudes idénticas son las mismas que para una solicitud única. ( De W3.org )

El efecto secundario del servidor aquí es que el servidor http se inicia una vez que se llama al método. No veo nada de malo con un método que haga esto.

Si necesita un patrón de diseño, supongo que podría exponer su httpServer como un singleton que se inicia cuando se inicializa.

Nick Williams
fuente
55
La función no es idempotente si llamarlo una vez inicia el servidor http y llamarlo por segunda vez no lo hace. Eso es literalmente lo contrario de la idempotencia. Sería idempotente si cada llamada iniciara un nuevo servidor http.
Polygnome
36
@Polygnome Wikipedia define la idempotencia como f (f (x)) = f (x) que generalmente coincide con la descripción de Nick. Aquí la entrada y salida de la función sería el estado implícito del servidor, por lo que el estado "servidor está funcionando" sería un punto fijo para esta función. Tal vez estoy malentendido el artículo de Wikipedia aquí, ¿podría vincular a otras referencias?
amon
16
@Polygnome: esta respuesta es correcta, la idempotencia se refiere a funciones para las que no importa si se llaman una o más de una vez, el resultado es siempre el mismo. Aquí, el resultado es uno que ejecuta el servicio http, independientemente del número de veces que se llama a la función.
Doc Brown
14
La confusión proviene del hecho de que la idempotencia en su núcleo es un concepto matemático aplicado a las funciones. La definición es agradable y simple para aquellos y también funciona bien para lenguajes funcionales puros. Tan pronto como introduce los efectos secundarios, se complica: debe considerar el entorno en el que ejecuta la función como un argumento (implícito) y salida de la función. Si ese estado no se modifica mediante nuevas llamadas a la función, es idempotente; de ​​lo contrario, no lo es. En este caso, el estado relevante es "está ejecutando el servidor http", por lo que la función es idempotente.
Voo
10
@Polygnome Lo siento, te equivocas. Idempotente significa que la solicitud tiene el mismo efecto si se ejecuta una vez o más de una vez. Iniciar N servidores HTTP si se reciben N duplicados de la solicitud definitivamente no es idempotente.
Kaz
7

Como el que implementar esta herramienta , startHttpServer, usted debe estar tratando de hacer que sea lo más simple, suave y sin costuras para uso ...

La lógica de la función

Técnicamente, al dividir startHttpServerla lógica en 2 funciones y llamarlas por separado , todo lo que haces es mover startHttpServer la idempotencia al código que llama a ambas funciones en su lugar ... Además, a menos que envuelva ambas lógicas en una tercera función (que es lo que hace startHttpServeren primer lugar), esto te obliga a escribir código sin DRY, duplicándolo exponencialmente en todas partes a las que deberías llamar startHttpServer. En resumen, startHttpServer tiene que llamarse a sí mismo la isHttpServerRunningfunción.

Entonces mi punto es:

  • Implemente la isHttpServerRunningfunción porque esto puede ser necesario independientemente de todos modos ...
  • Implemente startHttpServerhacer que se use isHttpServerRunningpara definir su próxima acción en consecuencia ...

Aún así, puede startHttpServerdevolver cualquier valor que el usuario de esta función pueda necesitar, por ejemplo:

  • 0 => falla de inicio del servidor
  • 1 => servidor iniciando con éxito
  • 2 => el servidor ya se inició

El nombre de la función.

En primer lugar, ¿cuál es el objetivo principal del usuario? Para iniciar el servidor HTTP , ¿verdad?

Fundamentalmente, no hay ningún problema al intentar comenzar algo que ya se ha iniciado, también conocido como AKA 1*1=1. Entonces, al menos para mí, llamarlo " ensureHttpServerIsRunning" parece no ser críticamente necesario, me importaría más cuánto tiempo, natural y memorable sea el nombre de la función.

Ahora, si desea saber cómo funciona en detalle la función bajo el capó, existe la documentación o el código fuente para eso, me refiero a cualquier otra función de biblioteca / marco / API / etc ...

Usted aprenderá la función de una vez mientras que escribir varias veces ...

De todos modos, me quedaría con lo startHttpServerque es más corto, más simple y más explícito que ensureHttpServerIsRunning.

ClemC
fuente
1
Especialmente porque el método debe tomar algunos argumentos de configuración, como el directorio raíz, la configuración de seguridad, posiblemente un número de puerto, estoy 100% cómodo con "iniciar" aquí.
user949300
@ user949300: A la persona que llama de "sureHttpServerIsRunning" no le importa la configuración, el directorio raíz, etc. Ese es el negocio de la persona que lo implementa.
gnasher729
2
@ gnasher729 Eso supone que el servidor http es un Singleton. Los solteros son malvados. Y posiblemente inapropiado aquí. Uno podría tener fácilmente múltiples servidores en múltiples puertos. Si realmente hay un solo servidor http, todo este método es, IMO, mal diseño. Es mejor simplemente iniciar el servidor una vez en la inicialización del programa.
user949300
2

Supongo que su colega quería decir que startHttpServerestá haciendo demasiado:

  • Comprobando si el servidor ya se está ejecutando,
  • Iniciando el servidor si es necesario.

Esas son dos partes de código no relacionadas. Por ejemplo, existe una situación similar cuando una aplicación de escritorio debe asegurarse de que no se esté ejecutando cuando se inicia; habrá una parte del código que maneja las instancias de la aplicación (por ejemplo, usando un mutex), y el código que iniciará el bucle de mensajes de la aplicación.

Esto significa que debe tener no uno, sino al menos dos métodos :

  • isHttpServerRunning: boolean
  • startHttpServer

El punto de entrada de la aplicación llamará al primer método, y luego al segundo si el valor de retorno es false. Ahora, cada método está haciendo una cosa y es fácil de entender.


¹ Si la lógica necesaria para saber si el servidor ya se está ejecutando es demasiado compleja, puede requerir una separación adicional en varios métodos.

Arseni Mourzenko
fuente
21
Ahora, otra cosa que llama a las dos funciones es hacer dos cosas, además, exponer las funciones por separado puede introducir condiciones de carrera. No hay almuerzo gratis.
cuál es el
1
Si eso es demasiado para el colega, buena suerte.
gnasher729
@ gnasher729: esta respuesta no contradice la suya; todo lo contrario, de hecho, podría ser una buena idea combinar el cambio de nombre del método con dividirlo en dos. Y mientras no veamos el código real, no sabemos cuán complejo es el código.
Doc Brown
10
Esta respuesta parece oponerse a la abstracción y SECO. ¿Qué pasa si startHttpServerse llama más de un lugar en el código? ¿Deberían copiarse múltiples líneas similares en todas partes? ¿Debería hacerse esto con todas las funciones? Muy pronto su programa tendrá un tamaño infinito.
JacquesB
3
Creo que el primer efecto secundario de este enfoque es que el inicio del startHttpServermétodo será más o menos similar if (isHttpServerRunning()){ return; }. Usted está afirmando una regla de negocios que "no es válido iniciar el servidor http si ya se está ejecutando", pero luego es responsabilidad de otra persona hacer cumplir esa regla. Ad-hoc y repetidamente en cada lugar donde puedan llamar startHttpServer.
Aroth
2

Como no especifica un idioma, en JavaScript muchas bibliotecas tienen una función de "una vez", por ejemplo, subrayado . Entonces, si eso le resulta familiar, llámelo "una vez" y posiblemente cambie el nombre de su método.

Yo mismo, viniendo más de Java, me vienen a la mente los términos "almacenamiento en caché" o "evaluación perezosa". "Idempotente" es técnicamente correcto y una buena opción, especialmente. si tienes un fondo más funcional.

user949300
fuente
Es posible que no sea "una vez", si se puede detener el servidor.
gnasher729
@ gnasher729. Me imagino un restartHttpServer()método raramente utilizado . Pero solo parando : ¿cuál es el caso de uso allí? ¿Te gustan los fallos de conexión esporádicos? :-)
user949300
No es necesario que exista un caso de uso para simplemente detener el servidor HTTP porque puede haber sido detenido por algo externo al programa en sí mismo: el servidor HTTP puede haber fallado y muerto, un administrador puede haberlo detenido manualmente por alguna razón, etc.
Dave Sherohman
Dave, un bloqueo es "excepcional" y debe manejarse como tal. Además, dado que un bloqueo puede ocurrir en cualquier momento , ¿no tendría que ser cada línea de su código ensureRunning()? :-) En cuanto a que el administrador lo detenga, tener este otro código reiniciando constantemente mientras el administrador intenta modificar o arreglar algo sería increíblemente molesto e incorrecto. Deje que el administrador lo reinicie, no el código.
user949300
-2

Yo prefiero startHttpServerIfNotIsRunning.

De esta manera, la condición ya se menciona claramente en el nombre del método. Ensureo me makeSureparece un poco vago, ya que no es una expresión técnica. Parece que no sabemos exactamente qué va a pasar.

Herr Derb
fuente
¿Asumo que no eres un hablante nativo? Porque asegurarse tiene la definición exacta de lo que buscamos. Pero aún así es justo que la documentación y las API no requieran que los niveles de hablantes nativos de inglés sean comprensibles.
Voo
1
Gracias, exactamente no lo que Ensuresignifica. Todavía no me gusta esta palabra para una expresión técnica. EnsureEs algo humano. Un sistema no puede garantizar nada, solo hará lo que se supone que debe hacer.
Herr Derb
2
@Voo: soy un hablante nativo y no estoy seguro de qué significa asegurar.
Jonathan Cast
Dado que todos los que publican aquí tienen acceso a Internet, ¿por qué no buscarían la definición de 'Asegurar' si no estuvieran seguros de lo que significaba? Un poco de trivia ... muchas personas intercambian el uso de 'Asegurar' y 'Asegurar' sin darse cuenta de que usar una de esas palabras podría hacerlas 'legalmente' responsables sin que la otra lo haga.
Dunk
@jcast: ¿En serio?
gnasher729
-3

Lo que su colega debería haberle dicho es que no tiene por qué escribir ese método. Ya se ha escrito muchas veces, y se ha escrito mejor de lo que es probable que lo escriba. Por ejemplo: http://docs.ansible.com/ansible/latest/systemd_module.html https://docs.saltstack.com/en/latest/ref/states/all/salt.states.service.html

Desde una perspectiva arquitectónica, tener un poco de código arbitrario para administrar un servidor web es cosa de pesadillas. A menos que administrar servicios sea exclusivamente lo que hace su código. Pero supongo que no escribiste monit (o kubernetes o ...).

Andrés
fuente
2
El lenguaje es Java y el método fue diseñado para iniciar un servidor embebido grizzly en una aplicación Jersey independiente. No puedo criticar la precisión de tu comentario, dado que no te di mucho contexto, pero asumiste mucho al hacer tu declaración. Está perfectamente bien tener un método que llame al embarcadero o al gris para iniciar el servidor.
John Calcote
1
Hay millones de aplicaciones comerciales que incluyen un servidor HTTP autohospedado para proporcionar una API. Y todos ellos necesitarán un código muy simple que realmente configure la biblioteca que están usando y proporcione información como qué interfaz vincular, qué puerto usar, configuraciones de seguridad, etc. Tener una startServerfunción o similar es algo poco común . Eso no significa que va a escribir los detalles esenciales de bajo nivel.
Voo