Comentários
Resposta
O problema que tenho visto ao manter o código que faz uso de sinalizadores é que o número de estados cresce rapidamente e quase sempre há estados não tratados. Um exemplo de minha própria experiência: eu estava trabalhando em um código que tinha esses três sinalizadores
bool capturing, processing, sending;
Esses três criaram oito estados (na verdade, havia dois outros sinalizadores também). Nem todas as combinações de valores possíveis foram cobertas pelo código, e os usuários estavam vendo bugs:
if(capturing && sending){ // we must be processing as well ... }
Acontece que havia situações em que a suposição na instrução if acima era falso.
Os sinalizadores tendem a aumentar com o tempo e ocultam o estado real de uma classe. É por isso que devem ser evitados.
Comentários
- +1, " devem ser evitados ". Eu acrescentaria algo sobre ', mas os sinalizadores são necessários em algumas situações ' (alguns podem dizer ' um mal necessário ')
- @TrevorBoydSmith Na minha experiência, eles não são, você só precisa de um pouco mais do que a potência cerebral média que você usaria para uma bandeira
- Em seu exemplo, deveria ser um único enum representando o estado, não 3 booleanos.
- Você pode ter um problema semelhante ao que eu ' m de frente agora. Além de cobrir todos os estados possíveis, dois aplicativos podem compartilhar o mesmo sinalizador (por exemplo, enviar dados do cliente). Nesse caso, apenas um Uploader usará o sinalizador, desligue-o e boa sorte para encontrar o problema no futuro.
Resposta
Aqui está um exemplo de quando os sinalizadores são úteis.
Eu tenho um trecho de código que gera senhas (usando um gerador de números pseudo-aleatórios criptograficamente seguro). O chamador do método escolhe se ou não a senha deve conter letras maiúsculas, minúsculas, dígitos, símbolos básicos, símbolos estendidos, símbolos gregos, cirílicos e unicode.
Com sinalizadores, chamar este método é fácil:
var password = this.PasswordGenerator.Generate( CharacterSet.Digits | CharacterSet.LowercaseLetters | CharacterSet.UppercaseLetters);
e pode até ser simplificado para:
var password = this.PasswordGenerator.Generate(CharacterSet.LettersAndDigits);
Sem sinalizadores, qual seria a assinatura do método?
public byte[] Generate( bool uppercaseLetters, bool lowercaseLetters, bool digits, bool basicSymbols, bool extendedSymbols, bool greekLetters, bool cyrillicLetters, bool unicode);
chamado assim:
// Very readable, isn"t it? // Tell me just by looking at this code what symbols do I want to be included? var password = this.PasswordGenerator.Generate( true, true, true, false, false, false, false, false);
Conforme observado nos comentários, outra abordagem seria usar uma coleção:
var password = this.PasswordGenerator.Generate( new [] { CharacterSet.Digits, CharacterSet.LowercaseLetters, CharacterSet.UppercaseLetters, });
Isso é muito mais legível em comparação com o conjunto de true
e false
, mas ainda tem duas desvantagens:
A principal desvantagem é que para permitir valores combinados, como CharacterSet.LettersAndDigits
você escreveria algo assim no método Generate()
:
if (set.Contains(CharacterSet.LowercaseLetters) || set.Contains(CharacterSet.Letters) || set.Contains(CharacterSet.LettersAndDigits) || set.Contains(CharacterSet.Default) || set.Contains(CharacterSet.All)) { // The password should contain lowercase letters. }
possivelmente reescrito assim:
var lowercaseGroups = new [] { CharacterSet.LowercaseLetters, CharacterSet.Letters, CharacterSet.LettersAndDigits, CharacterSet.Default, CharacterSet.All, }; if (lowercaseGroups.Any(s => set.Contains(s))) { // The password should contain lowercase letters. }
Compare isso com o que você tem usando sinalizadores:
if (set & CharacterSet.LowercaseLetters == CharacterSet.LowercaseLetters) { // The password should contain lowercase letters. }
O s Em segundo lugar, uma desvantagem muito pequena é que “não está claro como o método se comportaria se chamado desta forma:
var password = this.PasswordGenerator.Generate( new [] { CharacterSet.Digits, CharacterSet.LettersAndDigits, // So digits are requested two times. });
Comentários
- Eu acredito que OP está se referindo a variáveis booleanas ou inteiras para as quais você atribui um valor em certos lugares e, abaixo, você verifica em outros fazer algo ou não, como, por exemplo, usando
newItem = true
e algumas linhas abaixo deif (newItem ) then
- @MainMa Aparentemente lá ' sa 3o: a versão com 8 argumentos booleanos é o que pensei quando li " sinalizadores " …
- Desculpe, mas IMHO este é o caso perfeito para encadeamento de métodos ( en.wikipedia.org/wiki/Method_chaining), Além disso, você pode usar uma matriz de parâmetro (deve ser uma matriz associativa ou mapa), onde qualquer entrada nessa matriz de parâmetro omitida usa o comportamento de valor padrão para esse parâmetro. No final, a chamada via encadeamento de métodos ou matrizes de parâmetros pode ser tão sucinta e expressiva quanto sinalizadores de bits, também, nem toda linguagem tem operadores de bits (na verdade eu gosto de sinalizadores binários, mas usaria os métodos que acabei de mencionar).
- ' não é muito OOP, não é? I ' d criar uma interface ala: String myNewPassword = makePassword (randomComposeSupplier (new RandomLowerCaseSupplier (), new RandomUpperCaseSupplier (), novo RandomNumberSupplier)); com String makePassword (Supplier < Character > charSupplier); e Fornecedor < Caráter > randomComposeSupplier (Fornecedor < Caráter > … fornecedores); Agora você pode reutilizar seus fornecedores para outras tarefas, redigi-los da maneira que desejar e simplificar seu método generatePassword para que use o estado mínimo.
- @Dibbeke Fale sobre a reino dos substantivos …
Resposta
Um enorme bloco de funções é o cheiro , não as bandeiras. Se você definir o sinalizador na linha 5, verifique apenas o sinalizador na linha 354, então 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 é ruim.
Resposta
Normalmente sinalizadores pode ser completamente substituído por algum sabor do padrão de estratégia, com uma implementação de estratégia para cada valor possível da bandeira. Isso torna a adição de um novo comportamento muito mais fácil.
Em situações críticas de desempenho, o custo da indireção pode vir à tona e tornar necessária a desconstrução em sinalizadores claros. Dito isso, estou tendo problemas para lembrar um único caso em que realmente tive que fazer isso.
Resposta
Não, sinalizadores não são ruins ou um mal que deve ser refatorado a todo custo.
Considere Java “s Pattern.compile (String regex, sinalizadores int) chamada. Esta é uma máscara de bits tradicional e funciona. Dê uma olhada nas constantes em java e onde quer que você veja um monte de 2 n você sabe que há sinalizadores ali.
Em um mundo refatorado ideal, seria necessário usar um EnumSet em que as constantes são valores em um enum e conforme a documentação diz:
O desempenho de espaço e tempo desta classe deve ser bom o suficiente para permitir seu uso como uma alternativa de alta qualidade e segura de tipo para “sinalizadores de bit” tradicionais baseados em int.
Em um mundo perfeito, a chamada Pattern.compile torna-se Pattern.compile(String regex, EnumSet<PatternFlagEnum> flags)
.
Todos dito isso, ainda sinaliza.É muito mais fácil trabalhar com Pattern.compile("foo", Pattern.CASE_INSENSTIVE | Pattern.MULTILINE)
do que ter Pattern.compile("foo", new PatternFlags().caseInsenstive().multiline())
ou algum outro estilo de tentar fazer o que as sinalizações realmente são e bom para.
Os sinalizadores são freqüentemente vistos ao trabalhar com coisas de nível de sistema. Ao fazer a 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 terminará com um código pior do que se um usado aceitasse e entendesse a bandeira.
O problema ocorre quando as pessoas usam indevidamente as bandeiras jogando-as juntas e criando um conjunto frankenflag de todos os tipos de sinalizadores não relacionados ou tentando usá-los onde não sejam sinalizadores de forma alguma.
Resposta
Estou presumindo que estamos falando sobre sinalizadores dentro de assinaturas de método.
Usar um único sinalizador já é ruim o suficiente.
Não significará nada para seus colegas da primeira vez que 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 trata o seu método.
Passar um sinalizador 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:
if (flag) DoFlagSet(); else DoFlagNotSet();
Essa é uma separação inadequada de interesses e normalmente você pode encontrar uma maneira de contornar isso.
Normalmente, tenho dois métodos diferentes:
public void DoFlagSet() { } public void DoFlagNotSet() { }
Isso fará mais sentido com nomes de método que são 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ê ainda enfrentará o mesmo problema, pois seu método provavelmente está fazendo várias coisas.
Resposta
Sinalizadores e a maioria das variáveis de temperatura tem 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 o estado, devem ser refatorados para métodos de consulta. Os valores de estado (booleanos, ints e outros primários) devem quase -sempre- ficar ocultos como parte dos detalhes de implementação.
Sinalizadores que são usados para controle, roteamento e fluxo geral do programa também podem indicar o oportunidade de refatorar seções das estruturas de controle em estratégias ou fábricas separadas, ou o que quer que seja apropriado para a situação, que continue a fazer uso dos métodos de consulta.
Resposta
Quando falamos sobre 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 preciso sobre essas duas coisas, elas funcionarão muito bem.
Os sinalizadores podem funcionar muito bem se
- Você os tiver definido em um escopo apropriado. Por apropriado, quero dizer que o escopo não deve conter nenhum código que não precise / não deva modificá-lo. Ou pelo menos o código é seguro (por exemplo, pode não ser chamado diretamente de fora)
- Se houver necessidade de lidar com sinalizadores de fora e se houver muitos sinalizadores, podemos codificar o manipulador de sinalizadores como uma única maneira para modificar sinalizadores com segurança. Este manipulador de sinalização pode encapsular sinalizadores e métodos para modificá-los. Ele pode ser transformado em singleton e compartilhado entre as classes que precisam de acesso aos sinalizadores.
- E, finalmente, para manutenção, se houver muitos sinalizadores:
- Não há necessidade de dizer que deveriam siga a nomenclatura sensata
- Deve ser documentado com quais são os valores válidos (pode ser com enumerações)
- Deve ser documentado com QUE CÓDIGO MODIFICARÁ cada um deles, e também com QUE CONDIÇÃO resultará na atribuição de um determinado valor à bandeira.
- QUE CÓDIGO OS CONSUMIRÁ e QUE COMPORTAMENTO resultará para um determinado valor
Se houver muitos sinalizadores, um bom trabalho de design deve preceder os sinalizadores, então comece a desempenhar um papel-chave 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 haverá confusão.
Resposta
Presumi pela pergunta que o controle de qualidade estava significando variáveis sinalizadoras (globais), e não bits de um parâmetro de função.
Existem situações onde você não tem muitas outras possibilidades. Por exemplo, sem um sistema operacional, você tem que avaliar as interrupções.Se uma interrupção ocorre com muita frequência e você não tem tempo para fazer uma avaliação longa no ISR, não só é permitido, mas às vezes até mesmo a prática recomendada definir apenas alguns sinalizadores globais no ISR (você deve gastar o mínimo de tempo possível no ISR), e para avaliar esses sinalizadores em seu loop principal.
Resposta
Eu não acho qualquer coisa é um mal absoluto na programação, sempre.
Há outra situação em que os sinalizadores podem estar em ordem, que não foram mencionados aqui ainda …
Considere o uso de encerramentos neste snippet Javascript:
exports.isPostDraft = function ( post, draftTag ) { var isDraft = false; if (post.tags) post.tags.forEach(function(tag){ if (tag === draftTag) isDraft = true; }); return isDraft; }
A função interna, sendo passada ao “Array.forEach”, não pode simplesmente “retornar verdadeiro”.
Portanto, você precisa manter o estado fora com uma bandeira.
newItem = true
e algumas linhas abaixo deif (newItem ) then