¿Es una mala práctica de codificación crear algo en un get si no existe?

18

Por lo tanto, tengo un servicio web que tiene algo así como un getAccountlugar en el que devolvería un identificador a la cuenta si lo tuviera, de lo contrario arrojaría una excepción. El cliente siempre querrá crear una cuenta si se produce una excepción con la misma información con la que se realiza la obtención.

Estoy creando una biblioteca de conveniencia para los clientes que manejará todas las llamadas de servicio web en el interior para que no necesiten saber cómo hacer las llamadas ellos mismos.

Lo que me pregunto es en esta biblioteca si tuviera que crear una getAccount(accountName)que obtenga la cuenta si existe, y si no la crea y devuelva la información, ¿es algo malo? ¿Debo dejar que el cliente maneje las excepciones o simplemente nombrarlo como getOrCreateAccount? ¿Importa?

¿Es una mala práctica crear algo en una operación get?

Miguel
fuente
9
Por lo menos, lo nombraría getOrCreateAccounto similar.
Telastyn
44
Parece válido en el contexto de la inicialización perezosa. Sin embargo, depende si necesita usar ese patrón en su biblioteca.
Chris C
1
Me gusta el verbo acquirelike acquireAccount. No tiene un significado existente en ninguno de los protocolos principales que he encontrado, y tiene un tono imperativo que le queda bien. "Haz lo que tengas que hacer para adquirir uno de estos para mí. Solicítalo, construyelo, fingelo, robo, no me importa, solo consígueme uno o muere en el intento".
Dan Ross
2
"El cliente siempre querrá crear una cuenta", eso parece muy inusual. Si no puedo obtener la cuenta porque el usuario escribió mal su nombre de usuario, ciertamente no quiero crear una cuenta con el nombre incorrecto.
gnasher729
De acuerdo con la especificación javabean oracle.com/technetwork/articles/javaee/spec-136004.html getSomething() es para getters y setSomething()para setters. Imo cualquier cosa que hace algo más intelectual debe ser llamado otra cosa, es decir fetchSomething, obtainSomething, computeSomething, o doSomethingElseetc
ccpizza

Respuestas:

31

Si importa. En mi opinión, generalmente es una mala práctica crear algo en un procedimiento que no está documentado como que tiene los poderes de la creación. Ya sea nombrar el procedimiento getOrCreate...o tener un create...procedimiento separado y luego, si realmente lo desea, tener un getOrCreate...primer intento get..., y si eso falla, llama create...y luego llama get....

El usuario de la biblioteca probablemente no esperará get...que se cree el procedimiento si falla la operación de obtención. Si de repente descubren que sus llamadas de prueba get...crearon una tonelada completa de datos, probablemente se sorprenderán bastante. ¿Y cómo lo limpian? ¿Qué pasa si escriben código pensando que obtendrán un error si get...falla y quieren manejarlo a su manera?

FrustratedWithFormsDesigner
fuente
Gracias, create realmente devuelve la identificación que get también devolvería, por lo que no tendría que hacer get... create... get...solo los dos primeros. Hablaré con el cliente sobre si alguna vez requerirán la capacidad de simplemente llamar a un getsin querer crear
Mike
66
@ Mike: Sigo pensando que debería estar createen el nombre, solo para ser 100% claro sobre lo que está sucediendo.
FrustratedWithFormsDesigner
Sí, estoy de acuerdo, estaba planeando convertirlo en algo similar a getOrCreate porque mi pensamiento original era solo obtener, pero me di cuenta de que no está claro de inmediato que también esté creando algo en el fondo
Mike
1
Esto es muy tarde pero getOrCreatetiene prioridad en un marco web popular: docs.djangoproject.com/en/1.10/ref/models/querysets/…
tex
18

No, no es 'mala práctica'. Mientras usted y los otros desarrolladores estén de acuerdo en que así quiere que funcione, está bien. Después de todo, sería devolver una cuenta, que es lo que quieres. Que la cuenta se cree 'bajo el capó' es irrelevante para la persona que llama.

Gran maestro B
fuente
10
La única excepción es si es una API disponible públicamente. La comunidad en general ya ha acordado que GET es indempotente y nulipotente; en otras palabras, la misma acción se realiza cada vez, y es un método seguro que no cambia el estado del servidor. Esto está en el REST Wiki . Aparte de eso, si la API solo existe en su propio pequeño mundo, haga lo que sea aceptado por su grupo.
jmort253
9
No estoy de acuerdo, incluso para una API pública está bien, siempre que tenga sentido dentro del contexto de lo que se supone que debe hacer la API. No tiene mucho sentido crear múltiples entradas en la API cuando una sola haría el truco.
Gran maestro B
Para el registro, es una biblioteca completamente interna y tengo la capacidad de decirle al equipo que la usa cómo funciona
Mike
1
@ jmort253: Un servicio es nipipotente si no tiene efectos secundarios visibles para el cliente . El servidor puede hacer lo que le plazca.
Kevin Cline
1
@kevincline, creo que estaba pensando en términos de, digamos, cargar mi tarjeta de crédito. Si el servidor carga mi tarjeta en base a una solicitud GET pero aún así me da un resultado como "rechazado" cada vez que lo ejecuto, eso parece ir en contra del espíritu de GET. Dicho esto, entiendo tu punto. El servidor podría contar la cantidad de solicitudes GET y bloquearme después de 3 consultas, lo que me limitaría la tasa, pero sin cambiar ningún detalle de mi cuenta. Creo que es una distinción importante cuando uno dice que el estado del servidor no se modifica. Quizás debería decir "estado del cliente en el servidor" en su lugar
jmort253
10

Si getAccount()siempre puede devolver una cuenta, desde el punto de vista de la persona que llama, la cuenta existe, y siempre ha existido. No hay necesidad de getAccount()'crear' nada. La cuenta no tiene que almacenarse en ningún lugar hasta que sea diferente de la cuenta predeterminada.

Kevin Cline
fuente
+1 Generalmente, GetOrCreatees la semántica incorrecta, pero obtener un objeto que pueda existir "lógicamente", ya sea que exista o no físicamente, está bien. Como ejemplo, una matriz dispersa de elementos mutables puede no tener ningún almacenamiento asignado para el elemento 1,841,533, pero aún así permite que ese elemento sea "recuperado" creando un nuevo objeto, almacenándolo y devolviendo una referencia.
supercat
4

Tiene más sentido crear 3 métodos:

getAccount -> Que solo obtiene la cuenta.

createAccount -> Crea una cuenta.

getAccountAndCreateIfNeeded -> Elija su propio nombre;)

Por qué separación: tiene un método simple para obtener y crear. Ese es un método claro y comprobable para ambos. Para getAccount no es una excepción no encontrar la cuenta. Así que solo devuelve falso o algo así, se espera.

Luego puede usar ese valor de retorno en su función agrupada: getAccountAndCreateIfNeeded que ahora también es comprobable, debería devolver una cuenta siempre. Lo que sea que pidas.

Todos estos 3 métodos son claros, es exactamente claro lo que hacen y lo que devuelven. Puede llegar a acuerdos ahora con su equipo, pero este tipo de excepciones son terribles a largo plazo. Simplemente déjelos muy claros y no tendrá problemas.

Luc Franken
fuente
Además, de Clean Code: "Si su método no puede hacer lo que su nombre implica, entonces arroje una Excepción". Tendría un bloque Try / Catch dentro de 'GetOrCreateIfneeded' que arroja un GET fallido y luego tiene el 'createAccount' dentro del Throw para cualquier tipo de excepción que regrese para el get fallido.
Graham el
Podría sugerir un cuarto método: getAccountIfExistsque obtendría una cuenta o indicaría que no existe sin crear una nueva. El getAccountmétodo en sí mismo debe presuponer que la cuenta existe, y lanzar una excepción si no.
supercat
2

Depende de las circunstancias.

Por ejemplo, puede usarlo para cargar / crear instancias diferidas, aplazando la carga de datos o la creación de una instancia hasta que sea realmente necesaria. Esto normalmente es sensato ya que ahorra recursos que quizás no necesite (si la clase / datos nunca se necesitan, nunca se cargan).

Sin embargo, en este caso particular, diría que tener un método llamado getAccount que crearía una nueva cuenta si no existiera no sería una buena práctica. Si el usuario ha dado algunas credenciales para identificar una cuenta en particular y esa cuenta no se pudo encontrar, ¿eso significa que el usuario aún no es un cliente y debe tener una cuenta creada para ellos, o significa que las credenciales se escribieron incorrectamente? ¿y se le debe pedir al usuario que verifique que ha ingresado lo que quería ingresar?

Si tiene un método getAccount que crea una nueva cuenta si no puede identificar una, entonces no tiene otra opción. Si divide la creación de la cuenta y la salida de la cuenta en métodos separados, entonces tiene mucha más flexibilidad para decidir qué hacer si falla un intento de obtener una cuenta.

GordonM
fuente