Supongamos que tenemos un método foo(String bar)
que solo opera en cadenas que cumplen ciertos criterios; por ejemplo, debe estar en minúsculas, no debe estar vacío o tener solo espacios en blanco, y debe coincidir con el patrón [a-z0-9-_./@]+
. La documentación del método establece estos criterios.
¿Debería el método rechazar todas y cada una de las desviaciones de este criterio, o debería ser más indulgente con algunos criterios? Por ejemplo, si el método inicial es
public void foo(String bar) {
if (bar == null) {
throw new IllegalArgumentException("bar must not be null");
}
if (!bar.matches(BAR_PATTERN_STRING)) {
throw new IllegalArgumentException("bar must match pattern: " + BAR_PATTERN_STRING);
}
this.bar = bar;
}
Y el segundo método de perdón es
public void foo(String bar) {
if (bar == null) {
throw new IllegalArgumentException("bar must not be null");
}
if (!bar.matches(BAR_PATTERN_STRING)) {
bar = bar.toLowerCase().trim().replaceAll(" ", "_");
if (!bar.matches(BAR_PATTERN_STRING) {
throw new IllegalArgumentException("bar must match pattern: " + BAR_PATTERN_STRING);
}
}
this.bar = bar;
}
¿Debería cambiarse la documentación para indicar que se transformará y establecerá el valor transformado si es posible, o debería mantenerse el método lo más simple posible y rechazar todas y cada una de las desviaciones? En este caso, bar
podría ser configurado por el usuario de una aplicación.
El caso de uso principal para esto sería que los usuarios accedan a objetos desde un repositorio mediante un identificador de cadena específico. Cada objeto en el repositorio debe tener una cadena única para identificarlo. Estos repositorios podrían almacenar los objetos de varias maneras (servidor sql, json, xml, binario, etc.) y por eso traté de identificar el mínimo común denominador que coincidiría con la mayoría de las convenciones de nombres.
fuente
foo
función estricta que sea rigurosa en los argumentos que acepta, y tener una segunda función auxiliar que puede tratar de "limpiar" un argumento para ser utilizadofoo
. De esta manera, cada método tiene menos que hacer por sí mismo y se pueden administrar e integrar de manera más limpia. Si va por esa ruta, probablemente también sería útil alejarse de un diseño pesado de excepción; puede usar algo como en suOptional
lugar, y luego tener las funciones que consumenfoo
excepciones de lanzamiento si es necesario.Respuestas:
Su método debe hacer lo que dice que hace.
Esto evita errores, tanto de uso como de mantenedores que cambian el comportamiento más adelante. Ahorra tiempo porque los encargados del mantenimiento no necesitan dedicar tanto tiempo a descubrir qué está sucediendo.
Dicho esto, si la lógica definida no es fácil de usar, tal vez debería mejorarse.
fuente
foo
yfooForUncleanString
métodos donde este último hace las correcciones antes de pasarlo al primero.)Hay algunos puntos:
Recuerde que existen aserciones de depuración para diagnosticar errores lógicos en modo de depuración, lo que alivia principalmente cualquier inquietud de rendimiento.
Si implementa una interfaz de usuario, los mensajes de error amigables (incluyendo sugerencias y otra ayuda) son parte de un buen diseño.
Pero recuerde que las API son para programadores, no para usuarios finales.
Un experimento de la vida real para ser difuso y permisivo con la entrada es HTML.
Lo que resultó en que todos lo hicieran de manera ligeramente diferente, y la especificación, ahora está documentada, es un tomo gigantesco lleno de casos especiales.
Ver la ley de Postel (" Sé conservador en lo que haces, sé liberal en lo que aceptas de los demás ") y un crítico tocando eso ( o uno mucho mejor que MichaelT me hizo saber ).
fuente
El comportamiento de un método debe ser claro, intuitivo, predecible y simple. En general, deberíamos dudar mucho en hacer un procesamiento adicional en la entrada de la persona que llama. Tales suposiciones sobre lo que pretendía la persona que llama invariablemente tienen muchos casos extremos que producen un comportamiento no deseado. Considere una operación tan simple como unir rutas de archivos. ¡Muchas (o quizás incluso la mayoría) de las funciones de unión de ruta de archivo descartarán en silencio cualquier ruta anterior si una de las rutas que se unen parece estar enraizada! Por ejemplo,
/abc/xyz
unido con/evil
dará como resultado justo/evil
. Esto casi nunca es lo que pretendo cuando me uno a las rutas de archivos, pero debido a que no hay una interfaz que no se comporte de esta manera, me veo obligado a tener errores o escribir código adicional que cubra estos casos.Dicho esto, hay raras ocasiones en que tiene sentido que un método sea "indulgente", pero siempre debe estar dentro del poder de la persona que llama decidir cuándo y si estos pasos de procesamiento se aplican a su situación. Entonces, cuando haya identificado un paso de preprocesamiento común que desea aplicar a los argumentos en una variedad de situaciones, debe exponer interfaces para:
El último es opcional; solo debe proporcionarlo si lo usará una gran cantidad de llamadas.
Al exponer la funcionalidad sin formato, la persona que llama puede usarla sin el paso de preprocesamiento cuando sea necesario. Exponer el paso del preprocesador por sí solo permite a la persona que llama usarlo para situaciones en las que ni siquiera están llamando a la función o cuando desean preprocesar alguna entrada antes de llamar a la función (como cuando primero quieren pasarla a otra función). Proporcionar la combinación permite a las personas que llaman invocar a ambos sin problemas, lo cual es útil principalmente si la mayoría de las personas que llaman lo usarán de esta manera.
fuente
Como otros han dicho, hacer que la cadena coincida con "perdonar" significa introducir una complejidad adicional. Eso significa más trabajo en la implementación de la correspondencia. Ahora tiene muchos más casos de prueba, por ejemplo. Debe realizar un trabajo adicional para asegurarse de que no haya nombres semánticamente iguales en el espacio de nombres. Más complejidad también significa que hay más errores en el futuro. Un mecanismo más simple, como una bicicleta, requiere menos mantenimiento que uno más complejo, como un automóvil.
Entonces, ¿vale la coincidencia de cadena indulgente con todo ese costo adicional? Depende del caso de uso, como han señalado otros. Si las cadenas son algún tipo de entrada externa sobre la que no tiene control, y hay una ventaja definitiva para la coincidencia indulgente, puede valer la pena. Quizás la información proviene de usuarios finales que pueden no ser muy conscientes de los caracteres espaciales y las mayúsculas, y usted tiene un fuerte incentivo para hacer que su producto sea más fácil de usar.
Por otro lado, si la entrada viniera de, digamos, archivos de propiedades ensamblados por técnicos, quienes deberían entender eso
"Fred Mertz" != "FredMertz"
, estaría más inclinado a hacer que la correspondencia sea más estricta y ahorrar el costo de desarrollo.Sin embargo, creo que, en cualquier caso, es útil recortar y no tener en cuenta los espacios iniciales y finales. He visto demasiadas horas desperdiciadas en la depuración de este tipo de problemas.
fuente
Mencionas algunos de los contextos de los que proviene esta pregunta.
Dado eso, me gustaría que el método hiciera solo una cosa, afirma los requisitos en la cadena, deja que se ejecute en función de eso: no trataría de transformarlo aquí. Mantenlo simple y claro; documentarlo y tratar de mantener la documentación y el código sincronizados entre sí.
Si desea transformar los datos que provienen de la base de datos de usuarios de una manera más indulgente, coloque esa funcionalidad en un método de transformación separado y documente la funcionalidad vinculada .
En algún momento, los requisitos de la función deben medirse, documentarse claramente y la ejecución debe continuar. El "perdón", en este punto, es un poco mudo, es una decisión de diseño y yo diría que la función no muta su argumento. Hacer que la función mute la entrada oculta parte de la validación que se requeriría del cliente. Tener una función que hace la mutación ayuda al cliente a hacerlo bien.
El gran énfasis aquí es la claridad y documentar lo que hace el código .
fuente
fuente