As variáveis de flag são más? Os seguintes tipos de variáveis são profundamente imorais e é mau usá-las?
"variáveis booleanas ou inteiras às quais você atribui um valor em determinados locais e, abaixo, você faz check-in ou então para fazer algo ou não, como, por exemplo, usando
newItem = true
algumas linhas abaixoif (newItem ) then
"
Lembro-me de fazer alguns projetos nos quais negligenciei totalmente o uso de sinalizadores e acabei com uma melhor arquitetura / código; no entanto, é uma prática comum em outros projetos em que trabalho e, quando o código cresce e os sinalizadores são adicionados, o espaguete de código IMHO também cresce.
Você diria que há casos em que o uso de sinalizadores é uma boa prática ou mesmo necessário ?, ou você concorda que o uso de sinalizadores no código é ... sinalizador vermelho e deve ser evitado / refatorado; eu, apenas inicio funções / métodos que verificam estados em tempo real.
fonte
newItem = true
em seguida, algumas linhas abaixoif (newItem ) then
Respostas:
O problema que vi ao manter o código que faz uso de sinalizadores é que o número de estados cresce rapidamente e quase sempre existem estados não tratados. Um exemplo da minha própria experiência: eu estava trabalhando em algum código que tinha esses três sinalizadores
Esses três criaram oito estados (na verdade, havia duas outras bandeiras também). Nem todas as combinações de valores possíveis foram cobertas pelo código e os usuários estavam vendo bugs:
Aconteceu que havia situações em que a suposição na declaração if acima era falsa.
Os sinalizadores tendem a se acumular com o tempo e ocultam o estado real de uma classe. É por isso que eles devem ser evitados.
fonte
Aqui está um exemplo quando os sinalizadores são úteis.
Eu tenho um pedaço de código que gera senhas (usando um gerador de números pseudoaleatórios criptograficamente seguro). O chamador do método escolhe se a senha deve ou não conter letras maiúsculas, minúsculas, dígitos, símbolos básicos, símbolos estendidos, símbolos gregos, cirílicos e unicode.
Com sinalizadores, é fácil chamar esse método:
e pode até ser simplificado para:
Sem sinalizadores, qual seria a assinatura do método?
chamado assim:
Conforme observado nos comentários, outra abordagem seria usar uma coleção:
Isso é muito mais legível em comparação com o conjunto de
true
efalse
, mas ainda tem duas desvantagens:A principal desvantagem é que, para permitir valores combinados,
CharacterSet.LettersAndDigits
você escreveria algo assim noGenerate()
método:possivelmente reescrito assim:
Compare isso com o que você tem usando sinalizadores:
A segunda desvantagem muito pequena é que não está claro como o método se comportaria se fosse chamado assim:
fonte
newItem = true
em seguida, algumas linhas abaixoif (newItem ) then
Um enorme bloco funcional é o cheiro, não as bandeiras. Se você definir a bandeira na linha 5, verifique apenas a bandeira na linha 354, isso é ruim. Se você definir o sinalizador na linha 8 e verificar o sinalizador na linha 10, tudo bem. Além disso, um ou dois sinalizadores por bloco de código é bom, 300 sinalizadores em uma função são ruins.
fonte
Normalmente, os sinalizadores podem ser completamente substituídos por algum tipo de padrão de estratégia, com uma implementação de estratégia para cada valor possível do sinalizador. Isso facilita a adição de novos comportamentos.
Em situações críticas de desempenho, o custo da indireção pode surgir e tornar a desconstrução em sinalizadores claros necessários. Dito isto, estou tendo problemas para lembrar de um único caso em que realmente precisei fazer isso.
fonte
Não, as bandeiras não são ruins ou um mal que deve ser refatorado a todo custo.
Considere a chamada Pattern.compile do Java (String regex, int flags) . Esta é uma máscara de bits tradicional e funciona. Olhe para as constantes em java e onde quer que você veja um monte de 2 n você sabe que há bandeiras lá.
Em um mundo refatorado ideal, seria possível usar um EnumSet onde as constantes são valores em um enum e conforme a documentação diz:
Em um mundo perfeito, essa ligação para a Pattern.com se torna
Pattern.compile(String regex, EnumSet<PatternFlagEnum> flags)
.Tudo o que disse, ainda são bandeiras. É muito mais fácil trabalhar com o
Pattern.compile("foo", Pattern.CASE_INSENSTIVE | Pattern.MULTILINE)
que seria terPattern.compile("foo", new PatternFlags().caseInsenstive().multiline())
ou algum outro estilo de tentar fazer o que as bandeiras realmente são e são boas.Os sinalizadores geralmente são vistos ao trabalhar com itens no nível do sistema. Ao fazer interface com algo no nível do sistema operacional, é provável que haja um sinalizador em algum lugar - seja o valor de retorno de um processo, ou as permissões de um arquivo, ou os sinalizadores para abrir um soquete. Tentar refatorar essas instâncias em alguma caça às bruxas contra um cheiro de código percebido provavelmente acabará com um código pior do que se alguém usado aceitasse e entendesse a sinalização.
O problema ocorre quando as pessoas usam indevidamente bandeiras, jogando-as juntas e criando um conjunto de bandeiras franken de todos os tipos de bandeiras não relacionadas ou tentando usá-las onde não são bandeiras.
fonte
Estou assumindo que estamos falando de sinalizadores nas assinaturas de método.
Usar uma única bandeira já é ruim o suficiente.
Isso não significará nada para seus colegas na primeira vez em que eles o virem. Eles terão que olhar o código fonte do método para estabelecer o que ele faz. Você provavelmente estará na mesma posição alguns meses depois, quando esquecer do que se tratava o seu método.
Passar uma bandeira para o método normalmente significa que seu método é responsável por várias coisas. Dentro do método, você provavelmente está fazendo uma verificação simples nas linhas de:
Essa é uma separação pobre de preocupações e você normalmente pode encontrar uma maneira de contornar isso.
Normalmente, tenho dois métodos separados:
Isso fará mais sentido com nomes de métodos aplicáveis ao problema que você está resolvendo.
Passar vários sinalizadores é duas vezes pior. Se você realmente precisa passar vários sinalizadores, considere encapsulá-los em uma classe. Mesmo assim, você continuará enfrentando o mesmo problema, pois seu método provavelmente está fazendo várias coisas.
fonte
As bandeiras e a maioria das variáveis temporárias são um cheiro forte. Provavelmente, eles poderiam ser refatorados e substituídos por métodos de consulta.
Revisado:
Sinalizadores e variáveis temporárias ao expressar estado devem ser refatorados para consultar métodos. Os valores do estado (booleanos, ints e outros primitivos) devem quase sempre estar ocultos como parte dos detalhes da implementação.
Os sinalizadores usados para controle, roteamento e fluxo geral do programa também podem indicar a oportunidade de refatorar seções das estruturas de controle em estratégias ou fábricas separadas, ou o que for adequado à situação, que continuam a usar os métodos de consulta.
fonte
Quando falamos de sinalizadores, devemos saber que eles serão modificados ao longo do tempo de execução do programa e que afetarão o comportamento do programa com base em seus estados. Contanto que tenhamos controle puro sobre essas duas coisas, elas funcionarão muito bem.
Bandeiras podem funcionar muito bem se
Se houver muitas bandeiras, o bom trabalho de design deve preceder, pois as bandeiras começam a desempenhar um papel fundamental no comportamento do programa. Você pode ir para diagramas de estado para modelagem. Esses diagramas também funcionam como documentação e orientação visual ao lidar com eles.
Enquanto essas coisas estiverem no lugar, acho que não levará à bagunça.
fonte
Presumi da pergunta que o controle de qualidade significava variáveis de flag (global) e não bits de um parâmetro de função.
Há situações em que você não tem muitas outras possibilidades. Por exemplo, sem um sistema operacional, você deve avaliar as interrupções. Se uma interrupção ocorrer com muita frequência e você não tiver tempo para fazer uma avaliação longa no ISR, não é apenas permitido, mas às vezes até a melhor prática definir apenas alguns sinalizadores globais no ISR (você deve gastar o mínimo de tempo possível no ISR) e para avaliar esses sinalizadores no loop principal.
fonte
Eu acho que nada é absolutamente um mal na programação.
Há outra situação em que as bandeiras podem estar em ordem, que ainda não foram mencionadas aqui ...
Considere o uso de fechamentos neste snippet Javascript:
A função interna, sendo passada para "Array.forEach", não pode simplesmente "retornar true".
Portanto, você precisa manter o estado externo com uma bandeira.
fonte