Me gustaría agregar algo más que se sugiera en otras respuestas, pero no creo que se haya mencionado explícitamente:
@puck dice "Todavía no hay garantía de que el primer parámetro mencionado en el nombre de la función sea realmente el primer parámetro".
@cbojar dice "Usa tipos en lugar de argumentos ambiguos"
El problema es que los lenguajes de programación no entienden los nombres: solo son tratados como opacos, símbolos atómicos. Por lo tanto, al igual que con los comentarios de código, no hay necesariamente una correlación entre el nombre de una función y la forma en que realmente funciona.
Compare assertExpectedEqualsActual(foo, bar)
con algunas alternativas (de esta página y de otra parte), como:
# Putting the arguments in a labelled structure
assertEquals({expected: foo, actual: bar})
# Using a keyword arguments language feature
assertEquals(expected=foo, actual=bar)
# Giving the arguments different types, forcing us to wrap them
assertEquals(Expected(foo), Actual(bar))
# Breaking the symmetry and attaching the code to one of the arguments
bar.Should().Be(foo)
Todos estos tienen más estructura que el nombre detallado, lo que le da al lenguaje algo no opaco para mirar. La definición y el uso de la función también depende de esta estructura, por lo que no puede desincronizarse con lo que está haciendo la implementación (como puede hacer un nombre o comentario).
Cuando me enfrento o veo un problema como este, antes de gritar a mi computadora con frustración, primero me tomo un momento para preguntar si es "justo" culpar a la máquina. En otras palabras, ¿se le dio a la máquina suficiente información para distinguir lo que quería de lo que pedí?
Una llamada como assertEqual(expected, actual)
tiene tanto sentido como assertEqual(actual, expected)
, por lo que es fácil para nosotros mezclarlos y para que la máquina siga adelante y haga lo incorrecto. Si en su lugar usamos assertExpectedEqualsActual
, podría hacer que us sea menos probable que cometamos un error, pero no proporciona más información a la máquina (no puede entender el inglés y la elección del nombre no debería afectar) semántica).
Lo que hace que los enfoques "estructurados" sean más preferibles, como los argumentos de palabras clave, los campos etiquetados, los tipos diferenciados, etc., es que la información adicional también es legible por máquina , por lo que podemos hacer que la máquina sea incorrecta Usos y ayudarnos a hacer las cosas bien. El caso assertEqual
no es tan malo, ya que el único problema serían los mensajes inexactos. Un ejemplo más siniestro podría ser String replace(String old, String new, String content)
, que es fácil de confundir con String replace(String content, String old, String new)
que tiene un significado muy diferente. Un remedio simple sería tomar un par [old, new]
, lo que cometería errores de manera inmediata (incluso sin tipos).
Tenga en cuenta que incluso con los tipos, podemos encontrarnos a nosotros mismos 'sin decirle a la máquina lo que queremos'. Por ejemplo, el anti-patrón llamado "programación tipificada rigurosamente" trata a todos los datos como cadenas, lo que hace que sea fácil mezclar argumentos (como este caso), olvidarse de realizar algún paso (por ejemplo, escapar), romper accidentalmente las invariantes (por ejemplo, haciendo JSON irreparable), etc.
Esto también está relacionado con la "ceguera booleana", donde calculamos un montón de booleanos (o números, etc.) en una parte del código, pero cuando intentamos usarlos en otra no está claro de qué se trata. representando si los hemos mezclado, etc. Compare esto con, por ejemplo, enumeraciones distintas que tienen nombres descriptivos (por ejemplo, LOGGING_DISABLED
en lugar de false
) y que causan un mensaje de error si los confundimos.