Eu herdei um código terrível que incluí uma pequena amostra abaixo.
- Existe um nome para esse anti-padrão específico?
Quais são algumas recomendações para refatorar isso?
// 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) { }
Respostas:
O código é ruim não apenas pelos números mágicos , mas porque combina vários significados no código de retorno, ocultando dentro de seu significado um erro, um aviso, uma permissão para criar uma sessão ou uma combinação dos três, o que o torna um entrada ruim para tomada de decisão.
Eu sugeriria a seguinte refatoração: retornando uma enumeração com os possíveis resultados (conforme sugerido em outras respostas), mas adicionando à enumeração um atributo indicando se é uma negação, uma renúncia (deixarei você passar desta última vez) ou se estiver OK (PASS):
==> LoginResult.java <==
==> Severity.java <==
==> Test.java <==
Saída para Test.java mostrando a gravidade de cada LoginResult:
Com base no valor da enumeração e na sua gravidade, você pode decidir se a criação da sessão continua ou não.
EDITAR:
Como resposta ao comentário de @T.Sar, alterei os possíveis valores da gravidade para PASS, WAIVER e DENIAL em vez de (OK, WARNING e ERROR). Dessa forma, fica claro que um DENIAL (anteriormente ERRO) não é um erro em si e não deve necessariamente se traduzir em gerar uma exceção. O chamador examina o objeto e decide se deve ou não lançar uma exceção, mas DENIAL é um status de resultado válido resultante da chamada
processLogin(...)
.fonte
Severity
Este é um exemplo de obsessão primitiva - usando tipos primitivos para tarefas "simples" que acabam se tornando não tão simples.
Isso pode ter começado como um código que retornou a
bool
para indicar sucesso ou falha, depois se transformou emint
quando havia um terceiro estado e, eventualmente, se tornou uma lista completa de condições de erro quase não documentadas.A refatoração típica para esse problema é criar uma nova classe / struct / enum / objeto / o que puder representar melhor o valor em questão. Nesse caso, você pode considerar mudar para um
enum
que contenha as condições do resultado ou mesmo uma classe que possa conter umbool
para êxito ou falha, uma mensagem de erro, informações adicionais etc.Para obter mais padrões de refatoração que possam ser úteis, dê uma olhada na Folha de dicas Smells to Refactorings da Industrial Logic .
fonte
Eu chamaria isso de um caso de "números mágicos" - números que são especiais e não têm significado óbvio por si mesmos.
A refatoração que eu aplicaria aqui é reestruturar o tipo de retorno em uma enumeração, pois encapsula a preocupação do domínio em um tipo. Lidar com os erros de compilação resultantes disso deve ser possível aos poucos, pois as enumerações java podem ser ordenadas e numeradas. Mesmo se não, não deve ser difícil lidar com eles diretamente, em vez de recair nas ints.
fonte
if (processLogin(..) == 3)
Esse é um código particularmente desagradável. O antipadrão é conhecido como "código de retorno mágico". Você pode encontrar uma discussão aqui .
Muitos dos valores de retorno indicam estados de erro. Há um debate válido sobre a utilização do tratamento de erros para controle de fluxo, mas no seu caso, acho que existem três casos: sucesso (código 4), sucesso, mas é necessário alterar a senha (código 5) e "não permitido". Portanto, se você não se importa em usar exceções para controle de fluxo, use exceções para indicar esses estados.
Outra abordagem seria refatorar o design para que você retorne um objeto "usuário", com um atributo "perfil" e "sessão" para um login bem-sucedido, um atributo "must_change_password", se necessário, e vários atributos para indicar por que o log -in falhou se esse era o fluxo.
fonte