Eu tenho um pedaço de código no qual itero um mapa até que uma determinada condição seja verdadeira e, posteriormente, uso essa condição para fazer mais algumas coisas.
Exemplo:
Map<BigInteger, List<String>> map = handler.getMap();
if(map != null && !map.isEmpty())
{
for (Map.Entry<BigInteger, List<String>> entry : map.entrySet())
{
fillUpList();
if(list.size() > limit)
{
limitFlag = true;
break;
}
}
}
else
{
logger.info("\n>>>>> \n\t 6.1 NO entries to iterate over (for given FC and target) \n");
}
if(!limitFlag) // Continue only if limitFlag is not set
{
// Do something
}
Sinto que definir uma bandeira e depois usá-la para fazer mais coisas é um cheiro de código.
Estou certo? Como eu poderia remover isso?
design-patterns
object-oriented
object-oriented-design
clean-code
Siddharth Trikha
fonte
fonte
entry
não é usado em nenhum lugar dentro do loop de funções, e só podemos adivinhar o quelist
é. ÉfillUpList
suposto para preencherlist
? Por que não o obtém como parâmetro?Respostas:
Não há nada de errado em usar um valor booleano para a finalidade a que se destina: registrar uma distinção binária.
Se me dissessem para refatorar esse código, provavelmente colocaria o loop em um método próprio para que a atribuição + se
break
transforma em areturn
; então você nem precisa de uma variável, você pode simplesmente dizerfonte
fillUpList()
há algum código (que o OP decide não compartilhar) que realmente usa o valorentry
da iteração; sem essa suposição, pareceria que o corpo do loop não usava nada da iteração do loop.Não é necessariamente ruim e, às vezes, é a melhor solução. Mas definir sinalizadores como este em blocos aninhados pode dificultar o código.
O problema é que você tem blocos para delimitar escopos, mas depois possui sinalizadores que se comunicam entre os escopos, quebrando o isolamento lógico dos blocos. Por exemplo, o valor
limitFlag
será falso semap
fornull
, portanto o código "faça alguma coisa" será executado semap
fornull
. Pode ser o que você pretende, mas pode ser um erro fácil de perder, porque as condições para esse sinalizador são definidas em outro lugar, dentro de um escopo aninhado. Se você conseguir manter as informações e a lógica dentro do escopo mais rígido possível, tente fazê-lo.fonte
if(map==null || map.isEmpty()) { logger.info(); return;}
Isso, no entanto, funcionará apenas se o código que vemos for o corpo inteiro de uma função e a// Do something
parte não for necessária caso o mapa é nulo ou vazio.Eu desaconselharia o raciocínio sobre 'odores de código'. Essa é apenas a maneira mais preguiçosa possível de racionalizar seus próprios preconceitos. Com o tempo, você desenvolverá muitos preconceitos, e muitos deles serão razoáveis, mas muitos serão estúpidos.
Em vez disso, você deve ter razões práticas (ou seja, não dogmáticas) para preferir uma coisa a outra e evitar pensar que deve ter a mesma resposta para todas as perguntas semelhantes.
"Código cheira" é para quando você não está pensando. Se você realmente pensa no código, faça o que é certo!
Nesse caso, a decisão poderia realmente ser de qualquer maneira, dependendo do código ao redor. Realmente depende do que você acha que é a maneira mais clara de pensar sobre o que o código está fazendo. (código "limpo" é aquele que comunica claramente o que está fazendo com outros desenvolvedores e facilita a verificação de que está correto)
Muitas vezes, as pessoas escrevem métodos estruturados em fases, nas quais o código determina primeiro o que ele precisa saber sobre os dados e depois age sobre eles. Se a parte "determinar" e a parte "agir sobre ela" são um pouco complicadas, pode fazer sentido fazer isso, e muitas vezes o "o que ele precisa saber" pode ser carregado entre fases nos sinalizadores booleanos. Eu realmente preferiria que você desse um nome melhor à bandeira. Algo como "largeEntryExists" tornaria o código muito mais limpo.
Se, por outro lado, o código "// Do Something" for muito simples, poderá fazer mais sentido colocá-lo dentro do
if
bloco em vez de definir um sinalizador. Isso aproxima o efeito da causa, e o leitor não precisa varrer o restante do código para garantir que o sinalizador retenha o valor que você definiria.fonte
Sim, é um cheiro de código (sugestão reduzida de todos que o fazem).
O principal para mim é o uso da
break
declaração. Se você não o utilizasse, estaria iterando sobre mais itens do que o necessário, mas usá-lo fornece dois pontos de saída possíveis do loop.Não é um problema importante no seu exemplo, mas você pode imaginar que, à medida que as condicionais ou condicionais dentro do loop se tornam mais complexas ou a ordem da lista inicial se torna importante, é mais fácil para um bug entrar no código.
Quando o código é tão simples quanto o seu exemplo, ele pode ser reduzido a um
while
loop ou mapa equivalente, construção de filtro.Quando o código é complexo o suficiente para exigir sinalizadores e quebras, ele estará sujeito a erros.
Assim como todos os odores de código: Se você vir um sinalizador, tente substituí-lo por um
while
. Se não conseguir, adicione testes de unidade extras.fonte
SO as with all code smells: If you see a flag, try to replace it with a while
Você pode elaborar isso com um exemplo?for (Map.Entry<BigInteger, List<String>> entry : map.entrySet())
porfor (Iterator<Map.Entry<BigInteger, List<String>>> iterator = map.entrySet().iterator(); iterator.hasNext() && !limitFlag; Map.Entry<BigInteger, List<String>> entry = iterator.next())
. Esse é um padrão bastante incomum que eu teria mais problemas para entendê-lo do que uma pausa relativamente simples.Basta usar um nome diferente de limitFlag que informe o que você está realmente verificando. E por que você registra alguma coisa quando o mapa está ausente ou vazio? limtFlag será falso, tudo o que importa. O loop é bom se o mapa estiver vazio, portanto, não é necessário verificar isso.
fonte
Definir um valor booleano para transmitir informações que você já possui é uma prática ruim na minha opinião. Se não houver uma alternativa fácil, provavelmente é indicativo de um problema maior, como um encapsulamento ruim.
Você deve mover a lógica do loop for para o método fillUpList para quebrá-lo se o limite for atingido. Em seguida, verifique o tamanho da lista diretamente depois.
Se isso quebra seu código, por quê?
fonte
Primeiro do caso geral: não é incomum usar um sinalizador para verificar se algum elemento de uma coleção atende a uma determinada condição. Mas o padrão que vi com mais freqüência para resolver isso é mover a verificação em um método extra e retornar diretamente a partir dela (como Kilian Foth descrito em sua resposta ):
Desde o Java 8, existe uma maneira mais concisa de usar
Stream.anyMatch(…)
:No seu caso, isso provavelmente seria assim (supondo
list == entry.getValue()
na sua pergunta):O problema no seu exemplo específico é a chamada adicional para
fillUpList()
. A resposta depende muito do que esse método deve fazer.Nota lateral: do jeito que está, a chamada para
fillUpList()
não faz muito sentido, porque não depende do elemento que você está iterando no momento. Eu acho que isso é uma consequência de remover o código real para se ajustar ao formato da pergunta. Mas exatamente isso leva a um exemplo artificial difícil de interpretar e, portanto, difícil de raciocinar. Por isso, é tão importante para fornecer um mínimo, completa e verificável exemplo .Então, eu assumo que o código real passa a corrente
entry
para o métodoMas há mais perguntas a serem feitas:
BigInteger
chaves? Se eles não estiverem vazios, por que você precisa preencher as listas? Quando já existem elementos na lista, não é uma atualização ou alguma outra computação nesse caso?Estas são apenas algumas perguntas que me vieram à mente quando tentei entender o fragmento de código. Então, na minha opinião, esse é o verdadeiro cheiro do código : seu código não comunica claramente a intenção.
Isso pode significar isso ("tudo ou nada" e atingir o limite indica um erro):
Ou pode significar isso ("atualização até o primeiro problema"):
Ou isto ("atualize todas as listas, mas mantenha a lista original se ela ficar muito grande"):
Ou até o seguinte (usando
computeFoos(…)
o primeiro exemplo, mas sem exceções):Ou pode significar algo completamente diferente… ;-)
fonte