Estoy en una situación difícil. Estoy trabajando en un repositorio que hace muchos cambios en la base de datos dentro de sus funciones.

La función con la que estoy tratando devuelve responseids (pero de una transformación, no de cualquier acción de la base de datos). Sin embargo, como efecto secundario, agrega un objeto que contiene esos responseIds a la base de datos.

Entonces, debería nombrarlo:

  • getResponseIds: Esto resalta los valores de retorno. Es una forma de pensar muy funcional, pero obviamente si tengo la función postToDB no tiene sentido usar getStatusOfPost
  • addResponseIdToDB: Esto resalta el efecto secundario, aunque realmente creo que muchas de mis funciones solo operan en la base de datos (y tienden a no devolver nada)
  • getAndAddResponseIdsToDB: Muy informativo, pero muy extenso.

¿Cuáles son los pros y los contras de las sugerencias anteriores? ¿O puedes hacer una sugerencia mejor tú mismo?

Comentarios

  • ¿Qué tal persistResponseIds?
  • Es posible que se sorprenda de lo lejos que ha llegado la multitud funcional con el equivalente de getStatusOfPost 🙂
  • getAndSaveResponseIds
  • responseIds = createResponseIds() parece claro y conciso. Usted get() algo que no ‘ no existía antes, por lo que create es el verbo apropiado. El material recién creado es lo que ‘ espera que devuelva una función create(). ¿O quizás ‘ me estoy perdiendo algo obvio?
  • @fattie Estoy de acuerdo en que el código debe ser auto-comentable. Los comentarios no deberían decirle qué hace el código. Los comentarios deben decirle por qué existe el código. Si refactorizar el código significa refactorizarlo ‘ s comentarios, tienes comentarios negativos. Estoy de acuerdo en que los nombres pueden ser oraciones largas y completas. Un buen nombre debe seguir buscando dentro de una función para que no te sorprenda. No debería ‘ t decirle cómo implementarlo. Debería decirle lo que espera el cliente. Hazlo y tendrás una abstracción que vale la pena.

Respuesta

Un nombre de función que contiene and está en el nivel incorrecto de abstracción .

Me inclino por addResponseIdToDB() porque, de lo contrario, el «efecto secundario» es una completa sorpresa. Sin embargo:

responseIds = addResponseIdToDB(); 

no deja a nadie sorprendido.

El principio de segregación de responsabilidad de consulta de comandos sostiene que esta no debería ser la única forma de obtener el objeto responseId. También debería haber una consulta que no cambie la base de datos que puede obtener este objeto.

Al contrario de Bertrand Meyer , no creo que esto signifique que el complemento debe devolver vacío. Solo significa que debería existir una consulta pura equivalente y ser fácil de encontrar para que la base de datos no se abuse innecesariamente mediante el uso de consultas de cambio de estado.

Dado que getResponseIds() debería existir y no comunicarse con la base de datos, el mejor nombre para un método que hace ambas cosas es en realidad addToDB(getResponseId()). Pero eso es solo si desea obtener toda la composición funcional al respecto.

Comentarios

  • Agregando a lo que se ha dicho, pero agregando una palabra que no ‘ t ha sido usada, diría que » y » es apropiado cuando una función hace algo que es atómico . Si hay un beneficio que se puede obtener de dos cosas que suceden atómicamente / juntas / a la vez, entonces sea explícito con cuáles son esas dos cosas en la función nombre es bueno. Así que estoy de acuerdo con su primera afirmación en general, pero no estoy de acuerdo sin ninguna salvedad.
  • » … no deja a nadie sorprendido. «. No estoy de acuerdo, ‘ me quedé sorprendido de que una variable booleana se llame responseIds y luego tengo que hacer un cambio mental para darme cuenta de que se suma y se obtiene todo en uno. Pero no estoy de acuerdo con @tintintong en que getAndAddResponseIdsToDB es demasiado largo; es ‘ aproximadamente la mitad de la longitud de muchos de mis nombres de funciones. Si una función hace dos cosas, dígalo en su nombre.
  • @Luaan De acuerdo, ¿a qué le cambiará el nombre getAndIncrement a entonces? otherIncrement?
  • @Luaan Entonces, ¿qué valor devuelve Increment, el valor preincremento o postincremento? ‘ no está claro cuál sería sin profundizar en los documentos.Creo que incrementAndGet y getAndIncrement son mucho mejores nombres de métodos, lo que creo que es un buen caso para » Y » en los nombres de los métodos, al menos en el caso específico de los métodos que tienen efectos secundarios que podrían afectar el valor de retorno ..
  • @Luaan Cuando dices » Increment devuelve el valor incrementado » ¿te refieres al valor preincremento? Entendido, te entendí mal al principio … que en realidad es un gran ejemplo de por qué getAndIncrement y incrementAndGet son mejores nombres que simplemente increment, incluso cuando se considera individualmente

Respuesta

Como otros han mencionado, usar and en el nombre de una función, implica automáticamente que una función está haciendo al menos dos cosas, lo que generalmente indica que está haciendo demasiado o está haciendo algo en el lugar equivocado.

Usar la cláusula and en el nombre de una función, sin embargo, en mi experiencia, puede tener sentido cuando hay algunos pasos en un determinado flujo que debe realizarse en secuencia o cuando todo el cálculo sea significativo.

En cálculo numérico, esto puede tener mucho sentido:

normalizeAndInvert(Matrix m)

Quiero decir, quién sabe, ¿verdad? Si necesitas calcular algunas … digamos, trayectorias de iluminación en gráficos por computadora y en cierto en la etapa, debe normalizar e invertir una determinada matriz, y se encuentra escribiendo constantemente:

m = normalize(m), seguido de invert(m), solo como un ejemplo simple, la introducción de la abstracción de normalizar e invertir, puede ser bueno desde una perspectiva de legibilidad.

Hablando semánticamente, cosas como divideAndConquer() etc, probablemente se desaconseja que se escriba explícitamente, pero, bueno, el And es básicamente necesario.

Comentarios

  • Las funciones normalmente hacen más de una cosa, pero esas deben ser pequeñas cosas que suman una gran cosa que ‘ s significativo en un nivel superior. normalizeAndInvert podría ser computeLighting y divideAndConquer podría ser applyMachiavelli
  • Obviamente 🙂 Solo indicando que primero crear una función con un » y » en el nombre podría venir como una abstracción natural basada en un contexto particular. La refactorización a través del » El método de cambio de nombre » debería venir inmediatamente después;)
  • @BrunoOliveira Excepto que a veces los dos pasos son en sí mismos un componente de muchas cosas, cambiar el nombre no es ‘ t el enfoque correcto. Y debería ser poco común, pero nunca usarlo significa que ‘ se está repitiendo.

Responder

Yo diría que está bien en general, pero no es aceptable en todos los casos.

Por ejemplo, se podría argumentar en contra de and en un nombre de función basado en el principio de responsabilidad única también conocido como S en SOLID, porque el and podría implicar múltiples responsabilidades. Si el and realmente significa que la función está haciendo dos cosas, entonces probablemente quieras pensar con cuidado sobre lo que está sucediendo.

Un ejemplo de una biblioteca que utiliza and en los nombres de las funciones que se pueden encontrar en Java «s concurrencia y aquí and es en realidad una parte muy importante de lo que está sucediendo y coincide estrechamente con lo que describe en su publicación donde se cambia el estado y se devuelve el estado. Así que claramente hay algunas personas ( y algunos casos de uso) donde se considera aceptable.

Comentarios

  • » el and podría implicar múltiples responsabilidades «. Y en este caso sí lo indica. La función obtiene algunos ID de respuesta de algún lugar, los escribe en una base de datos y los devuelve. La necesidad de que » y » deberían al menos plantearle al OP la idea de que se necesitan dos funciones: una para obtener los ID y uno para escribirlos en la base de datos.
  • Si bien estoy de acuerdo en que and es un olor a código, el comportamiento básico parece legítimo: generalmente no es un mal idea para devolver algunos valores adicionales de un método que resultan ser resultados necesarios (intermedios) de llevar a cabo su función principal. Si la función principal de este método es agregar los ID de respuesta a la base de datos, devolver además los ID que ha agregado a la persona que llama es solo una característica de usabilidad ordenada y sin esfuerzo del método.
  • No ‘ creo que esto necesariamente viola el SRP. Siempre necesitará funciones que hagan varias cosas. La parte importante es que estas funciones llamen a otra función para cada cosa que quieran hacer en lugar de contener el código requerido directamente.
  • Si la función hace dos cosas, entonces el nombre debería reflejarlo.

Responder

De hecho, esa es una pregunta difícil de responder, como pueden atestiguar numerosos comentarios. La gente parece tener opiniones y consejos contradictorios sobre lo que es un buen nombre.

Me gustaría agregar mis 2 centavos a este hilo de 2 meses porque creo que puedo agregar más colores a lo que ya se sugirió.

Nombrar es un proceso

Todo esto me recuerda la excelente guía de 6 pasos: Nombrar como un proceso .

Un nombre de función deficiente es uno que sorprende y te das cuenta de que no puedes confiar. Un buen nombre es difícil de acertar a la primera. Se vuelve más fácil con la experiencia.

6 pasos iterativos para construir un buen nombre

  1. Reemplazar un nombre sorprendente con una tontería obvia como appleSauce(). Suena tonto, pero es temporal y hace evidente que no se puede confiar en el nombre.
  2. Busque un nombre honesto , según lo que comprenda de lo que hace la función. Digamos que todavía no entendió la inserción en la parte de la base de datos, getResponseIdsAndProbablyUseDb sería una opción.
  3. Obtener para ser completamente honesto , por lo que el nombre de la función dice todo lo que hace la función (getAndAddResponseIdsToDB o getResponsesButAlsoAddCacheOfTheResponsesToTheDb de @Fattie son excelentes ejemplos)
  4. Ir a «hace lo correcto» que es básicamente la parte en la que divide su función a lo largo del «Y». Entonces, en realidad tiene getResponseIds y addResponseIdsToDb.
  5. Obtener un nombre «que revela la intención» que resuelve el punto de «Siempre quiero obtener el ID de respuesta I insertado en la base de datos después de hacerlo «. Deje de pensar en los detalles de implementación y cree una abstracción que use las 2 funciones más pequeñas para construir otra cosa. Este es el nivel de abstracción más alto mencionado por @candied_orange. Para e xample createResponseIds podría hacerlo y será la composición de getResponseIds y addResponseIdsToDb.
  6. Accede a la abstracción de tu dominio . Esto es difícil, depende de tu negocio. Se necesita tiempo para escuchar el lenguaje de su empresa para hacerlo bien. Eventualmente, puede terminar con el concepto de un Response y Response.createIds() tendrían sentido. O tal vez ResponseId sea algo valioso y tendrías una fábrica para crear muchos. La inserción en la base de datos sería un detalle de implementación de un repositorio, etc. Sí, el diseño sería más abstracto. Sería más rico y te permitiría expresarte más. Nadie fuera de su equipo puede decirle cuál debería ser la abstracción de dominio correcta en su situación. Depende .

En su caso, «ya ha descubierto 1 y 2, por lo que probablemente debería ir al menos a 3 (use» Y «) o más. Pero ir más allá no es solo una cuestión de nombrar, en realidad dividiría las responsabilidades.

Por lo tanto, las diferentes sugerencias aquí son válidas

Básicamente:

  • Sí, los nombres de funciones sorprendentes son terribles y nadie quiere lidiar con eso
  • Sí, «Y» es aceptable en el nombre de una función porque es mejor que un nombre engañoso
  • Sí, el nivel de abstracción es un concepto relacionado y es importante

No tienes que pasar a la etapa 6 de inmediato, está bien permanecer en etapa 2, 3 o 4 hasta que lo sepa mejor!


Espero que sea útil para dar una perspectiva diferente sobre lo que parece un consejo contradictorio. Creo que todos apuntan al mismo objetivo ( nombres no sorprendentes), pero se detienen en diferentes etapas, según su propia experiencia.

Encantado de responder si tiene alguna pregunta 🙂

Responder

Su función hace dos cosas:

  1. Obtiene un conjunto de ID a través de una transformación y los devuelve a la persona que llama,
  2. Escribe ese conjunto de ID en una base de datos.

Recuerde el principio de responsabilidad única aquí: debe apuntar a una función tener una responsabilidad, no dos. Tiene dos responsabilidades, por lo que tiene dos funciones:

getResponseIds: obtiene un conjunto de ID a través de una transformación y se los devuelve al llamador
addResponseIdToDB: toma un conjunto de ID y los escribe en una base de datos.

Y todo el problema de cómo llamar a una sola función con múltiples responsabilidades desaparece y la tentación de poner and en los nombres de las funciones también desaparece.

Como una ventaja adicional, debido a que la misma función ya no es responsable de dos acciones no relacionadas, getResponseIds podría quitarse de su código de repositorio (donde no pertenece ya que no realiza ninguna actividad relacionada con la base de datos), en una clase / módulo de nivel empresarial independiente, etc.

Comentarios

  • Eso ‘ s seguramente cierto, pero te encuentras repitiendo un fragmento usando estos dos métodos SRP muchas veces, entonces probablemente deberías escribir un método trivial haciendo esto para que DRY, no ‘ ¿verdad?
  • @maaartinus, si se encuentra llamando a estos dos métodos en muchos lugares, es probable que tenga un problema con la falta de cohesión en su código. Crear una función » DRY » luego enmascara esta falta de cohesión.

Respuesta

Tener un nombre de función compuesto es aceptable, por lo que el esquema de nomenclatura que use dependerá de su contexto. Hay puristas que le dirán que lo rompa, pero yo argumentaré lo contrario.

Hay dos razones para tratar de evitar tales cosas:

  • Pureza: una función que hace dos cosas, debe convertirse en dos funciones que hacen una cosa cada una.
  • Tamaño léxico: un bigUglyCompositeFunctionName se vuelve difícil de leer.

El argumento de pureza suele ser el que se centra. Como heurística general vaga, dividir funciones es algo bueno. Ayuda a compartimentar lo que está sucediendo, lo que facilita su comprensión. Es menos probable que haga trucos «inteligentes» al compartir variables que conducen a un acoplamiento entre los dos comportamientos.

Entonces, lo que debe preguntarse es «¿debería hacerlo de todos modos?» Digo que dividir las cosas es una heurística vaga, por lo que realmente solo necesitas un argumento decente para ir en contra.

Una de las principales razones es que es beneficioso pensar en las dos operaciones como una sola. El ejemplo definitivo de esto se encuentra en operaciones atómicas: compare_and_set. compare_and_set es una piedra angular fundamental de la tecnología atomica (que es una forma de realizar subprocesos múltiples sin bloqueos) que es lo suficientemente exitosa como para que muchos estén dispuestos a pensar en ella como el piedra angular. Hay «y» en ese nombre. Hay una muy buena razón para esto. El objetivo de esta operación es que hace la comparación y el ajuste como una operación indivisible. Si lo dividiera en compare() y set(), anularía toda la razón por la que la función existía en primer lugar, porque dos compares() podría ocurrir consecutivamente antes de los set() s.

También vemos esto en el rendimiento. Mi ejemplo favorito es fastInvSqrt . Este es un famoso algoritmo de Quake que calcula 1 / sqrt (x). Combinamos las operaciones «inversa» y «raíz cuadrada» porque al hacerlo nosotros un aumento masivo del rendimiento. Hacen una aproximación de Newton que hace ambas operaciones en un solo paso (y sucede que lo hacen en matemáticas enteras en lugar de punto flotante, que importaba en la época). Si tuviera que hacer inverse(sqrt(x)), ¡sería mucho más lento!

Hay ocasiones en las que el resultado es más claro. He escrito varias API que involucran subprocesos donde uno tiene que tener mucho cuidado con cosas como interbloqueos. No quería que mi usuario estuviera al tanto de los detalles de implementación interna (especialmente porque podría cambiarlos), así que escribí la API con algunas funciones «y» que evitaban que el usuario tuviera que mantener un bloqueo durante más de una llamada de función. Esto significa que nunca necesitaron saber cómo estaba manejando la sincronización multiproceso bajo el capó. De hecho, la mayoría de mis usuarios ni siquiera sabían que estaban en un entorno multiproceso.

Entonces, aunque la regla general es dividir las cosas, siempre puede haber una razón para acoplarlas. Por ejemplo , ¿puede su usuario romper su arquitectura agregando a una base de datos varias veces sin los «get» intermedios? Si cada una de las respuestas registradas en la base de datos necesita tener un identificador único, usted podría tener problemas si el usuario pudiera hacerlo. de la misma forma, ¿alguna vez querría que un usuario «obtenga» sin registrar los resultados en la base de datos? Si la respuesta es «sí», divídalos para que el usuario pueda acceder a la función «obtener». Sin embargo, si el la seguridad de su aplicación se rompe si el usuario puede «obtener» sin que el resultado se registre en la base de datos, realmente debe mantener las funciones juntas.

Ahora, en cuanto a los problemas léxicos, el nombre de su función debe describir qué el usuario necesita saberlo. Comencemos con la parte «obtener».Es bastante fácil eliminar el «get» del nombre de una función, porque todos tus ejemplos mostrarán una asignación: int id = addResponseIdToDB() «. En todos los lugares donde se usa la función, terminarás documentando el hecho de que la función devolvió un valor.

Asimismo, «agregar» puede ser opcional. Usamos el término «efecto secundario» como un término que lo abarca todo, pero hay diferentes matices. Si las entradas de la base de datos son simplemente un registro, puede que no haya razón para resaltarlas. Nunca vemos funciones como playMusicAndSendPersonalInformationToOurCorporateServers. Es simplemente playMusic, y si tiene suerte, la documentación puede mencionar algo sobre una conexión de socket a través de Internet. Por otro lado, si se espera que los usuarios llamen a esta función con el propósito de agregar cosas a la base de datos, entonces «agregar» es esencial. No lo saque.

Ahora, he escrito muchas razones para hacer todas las combinaciones posibles de lo que está pidiendo. Intencionalmente no he respondido a la pregunta porque hay que hacer una elección. No es una regla a seguir. Estás creando una API.

Dicho esto, mi instinto es que es probable que addResponseIdToDB sea la mejor respuesta. En la mayoría de los casos, la parte «get» es un efecto secundario lo suficientemente obvio que no genera el ruido adicional causado por escribirlo en todas partes. Sin embargo, hay algunos lugares donde podría considerarlo importante:

  • Si el «get» es caro: si tiene que hacer un «get» que implica buscar algo a través de Internet y luego agregarlo a una base de datos de caché local, por supuesto, el «get» es importante. Es lo que el usuario está intentando hacer.
  • Si necesita dejar en claro que el usuario necesita prestar atención al valor. Si el usuario quiere usar la variable, se dará cuenta de que la API la devuelve y la usará. Sin embargo, desea tener cuidado con el usuario que no sabe que lo necesita. Por ejemplo, si tiene que «cerrar» esta operación por ID más tarde para liberar memoria, es posible que desee llamar la atención sobre el hecho de que Estás haciendo algo. En estos casos, es posible que desee buscar un verbo que no sea «obtener». «get» a menudo implica funciones idempotentes (llamarlo de nuevo no hace nada). Si devuelve recursos, otros verbos como «crear» o «adquirir» son buenos. Este mecanismo de falla en particular es un problema importante en C cuando se manejan excepciones . C carece del mecanismo de captura / lanzamiento de C ++, por lo que se basa en códigos de retorno. Los desarrolladores son famosos simplemente por no verificar estos códigos de retorno y meterse en situaciones realmente malas como desbordamientos de búfer debido a ello.
  • Simetría: a veces se diseña una API para que tenga una simetría léxica. Se configuran las palabras para que aparezcan en pares u otros patrones, y se elaboran los comandos para que sea fácil de identificar visualmente. si el patrón se ha seguido o no. Yo diría que esto es raro, pero no es inaudito. Existe una razón por la que al cerrar las etiquetas XML se repite el nombre de la etiqueta (como < foo > y < / foo >).

Comentarios

  • Para personas que no ‘ No sé las matemáticas: tanto 1 / x como sqrt (x) son funciones bastante lentas. Usando algunas matemáticas inteligentes, podemos calcular 1 / sqrt (x) más rápido que una división o una raíz cuadrada. En realidad, es mucho más rápido que la forma más rápida de calcular sqrt (x) es calcular 1 / sqrt (x) con una implementación inteligente y multiplicar el resultado por x.
  • Como en física, donde las unidades elementales ya no son metro y segundo, sino segundo y velocidad de la luz, porque podemos medir la velocidad de la luz con más precisión que la longitud de un metro.

Respuesta

¿Cuál es el mejor intención de esta metanfetamina od? ¿Por qué agregar estos identificadores transformados a la base de datos, es con el propósito de almacenar en caché? Trabajaré bajo esa suposición.

Parece que la intención del método es realmente solo obtener los ID de respuesta transformados (ya sea haciendo la transformación o obteniéndolos de un caché), por lo que hay el nombre de su método:

getTransformedResponseIds o menos detallado getResponseIds()

Un método con este nombre podría almacenar en caché o tal vez no, y eso se puede documentar, pero el nombre de su método no debería vincularlo a una implementación específica; ¿Qué pasa si decide dejar de usar una base de datos y en su lugar almacenarlos en otro lugar, como solo temporalmente en la memoria?

Los efectos secundarios deberían ser solo eso, efectos secundarios, probablemente deberían estar documentados, pero en realidad no deberían afectar la intención central (o el éxito) o el nombre de la función. Si la obtención del valor del caché falla (o si configura para omitir el almacenamiento en caché) eso no debería ser un problema crítico, el método debería simplemente volver a calcular, almacenar en caché (o no) de forma transparente y devolver el nuevo valor.

A veces, usar un «Y» es útil para transmitir la intención, como con los métodos Java getAndIncrement y incrementAndGet, por lo que eso «ciertamente no está fuera de la mesa.

Respuesta

Dices que la función devuelve algo» de una transformación, no de cualquier acción de db «.

Esto sugiere que la función se comporta más como un constructor que como un captador. Sin embargo, los constructores tampoco deberían» causar efectos secundarios (gracias por el enlace, @ MechMK1 ).

Los métodos de fábrica, por otro lado, ya presuponen cierto nivel de desorden . Por ejemplo, una de las motivaciones para usar métodos de fábrica es que un método de fábrica:

Permite un código más legible en los casos en que existen varios constructores, cada uno para una razón diferente. – wikipedia

Asimismo,

Si necesita trabajar con los efectos secundarios, un método de fábrica se puede nombrar de tal manera que sea más claro cuáles son esos efectos secundarios. – ruakh

Considere convertir la función en un método de fábrica, utilizando el término «registrado» para alertar a los programadores sobre el almacenamiento de la base de datos:

  • Declaración: createLoggedResponseid(...) {...} // log object to db
  • Llamada: responseid = createLoggedResponseid(...);

Comentarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *