Heredé un código horrible que incluí una pequeña muestra a continuación.
- ¿Hay un nombre para este antipatrón particular?
¿Cuáles son algunas recomendaciones para refactorizar esto?
// 0=Need to log in / present username and password // 2=Already logged in // 3=Inactive User found // 4=Valid User found-establish their session // 5=Valid User found with password change needed-establish their session // 6=Invalid User based on app login // 7=Invalid User based on network login // 8=User is from an non-approved remote address // 9=User account is locked // 10=Next failed login, the user account will be locked public int processLogin(HttpServletRequest request, HttpServletResponse response, int pwChangeDays, ServletContext ServContext) { }
Respuestas:
El código es malo no solo por los números mágicos , sino porque fusiona varios significados en el código de retorno, ocultando dentro de su significado un error, una advertencia, un permiso para crear una sesión o una combinación de los tres, lo que lo convierte en un mal aporte para la toma de decisiones.
Sugeriría la siguiente refactorización: devolver una enumeración con los posibles resultados (como se sugiere en otras respuestas), pero agregar a la enumeración un atributo que indique si es una negación, una exención (lo dejaré pasar esta última vez) o si está bien (PASAR):
==> LoginResult.java <==
==> Severity.java <==
==> Test.java <==
Salida para Test.java que muestra la gravedad de cada LoginResult:
Según el valor de enumeración y su gravedad, puede decidir si la creación de la sesión continúa o no.
EDITAR:
Como respuesta al comentario de @T.Sar, cambié los posibles valores de la gravedad a PASS, WAIVER y DENIAL en lugar de (OK, WARNING y ERROR). De esta manera, está claro que una DENEGACIÓN (anteriormente ERROR) no es un error per se y no necesariamente debe traducirse en arrojar una excepción. La persona que llama examina el objeto y decide si lanzar o no una excepción, pero DENIAL es un estado de resultado válido resultante de la llamada
processLogin(...)
.fuente
Severity
Este es un ejemplo de obsesión primitiva : el uso de tipos primitivos para tareas "simples" que eventualmente no se vuelven tan simples.
Esto puede haber comenzado como un código que devolvió un
bool
para indicar éxito o fracaso, luego se convirtió en unint
cuando había un tercer estado y finalmente se convirtió en una lista completa de condiciones de error casi indocumentadas.La refactorización típica para este problema es crear una nueva clase / estructura / enumeración / objeto / lo que sea que pueda representar mejor el valor en cuestión. En este caso, puede considerar cambiar a un
enum
que contenga las condiciones del resultado, o incluso una clase que pueda contener unbool
mensaje de error, información adicional, etc.Para obtener más patrones de refactorización que puedan ser útiles, eche un vistazo a la hoja de trucos de Industrial Logic Smells to Refactorings .
fuente
Yo llamaría a eso un caso de "números mágicos", números que son especiales y no tienen un significado obvio por sí mismos.
La refactorización que aplicaría aquí es reestructurar el tipo de retorno a una enumeración, ya que encapsula la preocupación del dominio en un tipo. Tratar con los errores de compilación que se caen debe ser posible poco a poco, ya que las enumeraciones de Java se pueden ordenar y numerar. Incluso si no, no debería ser difícil lidiar con ellos directamente en lugar de recurrir a los ints.
fuente
if (processLogin(..) == 3)
Este es un código de código particularmente desagradable. El antipatrón se conoce como "códigos de retorno mágicos". Puedes encontrar una discusión aquí .
Muchos de los valores de retorno indican estados de error. Existe un debate válido sobre si utilizar el manejo de errores para el control de flujo, pero en su caso, creo que hay 3 casos: éxito (código 4), éxito pero necesita cambiar la contraseña (código 5) y "no permitido". Por lo tanto, si no le importa usar excepciones para el control de flujo, puede usar excepciones para indicar esos estados.
Otro enfoque sería refactorizar el diseño para que devuelva un objeto "usuario", con un atributo "perfil" y "sesión" para un inicio de sesión exitoso, un atributo "must_change_password" si es necesario y un conjunto de atributos para indicar por qué el registro -in falló si ese era el flujo.
fuente