Um colega meu afirma que os booleanos como argumentos de método não são aceitáveis . Eles devem ser substituídos por enumerações. No começo, não vi nenhum benefício, mas ele me deu um exemplo.
O que é mais fácil de entender?
file.writeData( data, true );
Ou
enum WriteMode {
Append,
Overwrite
};
file.writeData( data, Append );
Agora entendi! ;-)
Esse é definitivamente um exemplo em que uma enumeração como segundo parâmetro torna o código muito mais legível.
Então, qual sua opinião sobre esse assunto?
coding-style
boolean
enumeration
Thomas Koschel
fonte
fonte
Respostas:
Booleanos representam opções de "sim / não". Se você deseja representar um "sim / não", use um booleano, que deve ser auto-explicativo.
Mas se é uma escolha entre duas opções, nenhuma das quais é claramente sim ou não, então um enum pode às vezes ser mais legível.
fonte
setLightOn(bool)
.As enums também permitem modificações futuras, nas quais agora você deseja uma terceira opção (ou mais).
fonte
Use o que melhor modela seu problema. No exemplo que você dá, o enum é uma escolha melhor. No entanto, haveria outros momentos em que um booleano é melhor. O que faz mais sentido para você:
ou
Nesse caso, posso escolher a opção booleana, pois acho que é bastante clara e inequívoca, e tenho certeza de que meu bloqueio não terá mais de dois estados. Ainda assim, a segunda opção é válida, mas desnecessariamente complicada, IMHO.
fonte
Para mim, nem usar booleano nem enumeração é uma boa abordagem. Robert C. Martin captura isso muito claramente em sua dica de código limpo nº 12: Eliminar argumentos booleanos :
Se um método faz mais de uma coisa, você deve escrever dois métodos diferentes, por exemplo, no seu caso:
file.append(data)
efile.overwrite(data)
.Usar uma enumeração não torna as coisas mais claras. Não muda nada, ainda é um argumento de bandeira.
fonte
setVisible(boolean visible) { mVisible = visible; }
. Qual seria uma alternativa que você sugeriria?Eu acho que você quase respondeu a si mesmo, acho que o objetivo final é tornar o código mais legível e, neste caso, o enum fez isso, a OMI sempre é melhor olhar para o objetivo final em vez de regras gerais, talvez pense mais nisso como diretriz, ou seja, as enumerações geralmente são mais legíveis em código do que bools, ints etc., mas sempre haverá exceções à regra.
fonte
Lembra da pergunta que Adlai Stevenson fez ao embaixador Zorin na ONU durante a crise dos mísseis cubanos ?
Se o sinalizador que você possui no seu método é de tal natureza que você pode atribuí-lo a uma decisão binária , e essa decisão nunca se transformará em uma decisão de três ou de duas vias, escolha booleano. Indicações: sua bandeira é chamada isXXX .
Não o torne booleano no caso de algo que seja uma opção de modo . Sempre há mais um modo que você pensava ao escrever o método em primeiro lugar.
O dilema de mais um modo assombrou o Unix, onde os possíveis modos de permissão que um arquivo ou diretório pode ter hoje resultam em estranhos significados duplos de modos, dependendo do tipo de arquivo, propriedade etc.
fonte
Há duas razões pelas quais me deparei com isso ser uma coisa ruim:
Porque algumas pessoas escreverão métodos como:
Obviamente, isso é ruim porque é muito fácil misturar parâmetros e você não faz ideia do que está especificando. Apenas um bool não é tão ruim assim.
Como controlar o fluxo do programa por um simples ramo sim / não pode significar que você tem duas funções completamente diferentes que são agrupadas em uma de uma maneira estranha. Por exemplo:
Realmente, isso deve ser dois métodos
porque o código nestes pode ser totalmente diferente; eles podem ter que fazer todo tipo de manipulação e validação de erro diferentes, ou talvez até precisar formatar os dados de saída de maneira diferente. Você não pode dizer isso apenas usando
Write()
ou mesmoWrite(Enum.Optical)
(embora, é claro, você possa ter um desses métodos, basta chamar métodos internos WriteOptical / Mag, se desejar).Eu acho que só depende. Eu não faria muito acordo a não ser o número 1.
fonte
As enums são melhores, mas eu não chamaria os parâmetros booleanos de "inaceitáveis". Às vezes, é mais fácil colocar um pouco de booleano e seguir em frente (pense em métodos particulares etc.)
fonte
Os booleanos podem estar OK em idiomas que tenham parâmetros nomeados, como Python e Objective-C, pois o nome pode explicar o que o parâmetro faz:
ou:
fonte
Eu não concordaria que é uma boa regra . Obviamente, o Enum cria um código explícito ou detalhado melhor em algumas instâncias, mas, como regra, parece muito exagerado.
Primeiro, deixe-me dar o seu exemplo: a responsabilidade (e a capacidade) dos programadores de escrever um bom código não é realmente comprometida por ter um parâmetro booleano. No seu exemplo, o programador poderia ter escrito o mesmo código detalhado escrevendo:
ou eu prefiro mais geral
Segundo: O exemplo de Enum que você deu é apenas "melhor" porque você está passando um CONST. Provavelmente, na maioria das aplicações, pelo menos alguns dos parâmetros de tempo que são passados para as funções são VARIABLES. Nesse caso, meu segundo exemplo (fornecer variáveis com bons nomes) é muito melhor e o Enum teria dado poucos benefícios.
fonte
As enums têm um benefício definido, mas você não deve substituir todos os seus booleanos por enums. Existem muitos lugares onde verdadeiro / falso é realmente a melhor maneira de representar o que está acontecendo.
No entanto, usá-los como argumentos de método é um pouco suspeito, simplesmente porque você não pode ver sem se aprofundar nas coisas que eles deveriam fazer, pois permite que você veja qual é o verdadeiro / falso realmente significa
Propriedades (especialmente com inicializadores de objetos C # 3) ou argumentos de palavras-chave (à la ruby ou python) são uma maneira muito melhor de ir para onde você usaria um argumento booleano.
Exemplo de C #:
Exemplo de Ruby
Exemplo de Python
A única coisa em que consigo pensar onde um argumento de método booleano é a coisa certa a fazer é em java, onde você não tem argumentos de propriedades ou de palavras-chave. Esta é uma das razões pelas quais eu odeio java :-(
fonte
Embora seja verdade que, em muitos casos, as enums sejam mais legíveis e extensíveis que os booleanos, uma regra absoluta de que "os booleanos não são aceitáveis" é insensata. É inflexível e contraproducente - não deixa espaço para julgamento humano. Eles são um tipo básico incorporado na maioria dos idiomas porque são úteis - considere aplicá-lo a outros tipos internos: dizer, por exemplo, "nunca use um int como parâmetro" seria uma loucura.
Essa regra é apenas uma questão de estilo, não de potencial para erros ou desempenho em tempo de execução. Uma regra melhor seria "preferir enums a booleanos por razões de legibilidade".
Veja a estrutura .Net. Os booleanos são usados como parâmetros em vários métodos. A API .Net não é perfeita, mas não acho que o uso de booleano como parâmetros seja um grande problema. A dica de ferramenta sempre fornece o nome do parâmetro e você também pode criar esse tipo de orientação - preencha seus comentários XML sobre os parâmetros do método, eles aparecerão na dica de ferramenta.
Devo acrescentar também que existe um caso em que você deve refatorar claramente os booleanos para uma enumeração - quando você tem dois ou mais booleanos em sua classe ou nos parâmetros de método e nem todos os estados são válidos (por exemplo, não é válido tê-los ambos são verdadeiros).
Por exemplo, se sua classe tiver propriedades como
E é um erro ter os dois verdadeiros ao mesmo tempo, o que você realmente tem são três estados válidos, melhor expressos como algo como:
fonte
Algumas regras que seu colega pode aderir melhor são:
fonte
Um booleano só seria aceitável se você não pretender estender a funcionalidade da estrutura. O Enum é preferível porque você pode estender o enum e não interromper implementações anteriores da chamada de função.
A outra vantagem do Enum é que é mais fácil de ler.
fonte
Se o método fizer uma pergunta como:
Onde
argumentos de método booleano parecem fazer absolutamente perfeito sentido.
fonte
Depende do método. Se o método faz algo que obviamente é uma coisa verdadeira / falsa, então tudo bem, por exemplo, abaixo [embora não esteja dizendo que esse é o melhor design para esse método, é apenas um exemplo de onde o uso é óbvio].
No entanto, na maioria dos casos, como o exemplo que você mencionou, é melhor usar uma enumeração. Existem muitos exemplos no próprio .NET Framework em que essa convenção não é seguida, mas é porque eles introduziram essa diretriz de design bastante tarde no ciclo.
fonte
Isso torna as coisas um pouco mais explícitas, mas começa a ampliar massivamente a complexidade de suas interfaces - em uma simples escolha booleana, como acrescentar / substituir, parece um exagero. Se você precisar adicionar uma opção adicional (que não consigo pensar neste caso), sempre poderá executar um refatoramento (dependendo do idioma)
fonte
Enums certamente podem tornar o código mais legível. Ainda há algumas coisas a serem observadas (pelo menos em .net)
Como o armazenamento subjacente de uma enum é um int, o valor padrão será zero, portanto, verifique se 0 é um padrão sensato. (Por exemplo, estruturas têm todos os campos definidos como zero quando criados, portanto, não há como especificar um padrão diferente de 0. Se você não tiver um valor 0, não poderá nem testar a enum sem converter para int, o que seria estilo ruim.)
Se sua enumeração é privada ao seu código (nunca exposta publicamente), você pode parar de ler aqui.
Se suas enumerações forem publicadas de alguma forma no código externo e / ou salvas fora do programa, considere numerá-las explicitamente. O compilador os numera automaticamente de 0, mas se você reorganizar suas enumerações sem fornecer valores, poderá acabar com defeitos.
Eu posso escrever legalmente
Para combater isso, qualquer código que consuma uma enumeração que você não pode ter certeza (por exemplo, API pública) precisa verificar se a enumeração é válida. Você faz isso via
A única ressalva
Enum.IsDefined
é que ele usa reflexão e é mais lento. Ele também sofre de um problema de versão. Se você precisar verificar o valor da enumeração com frequência, seria melhor o seguinte:A questão do controle de versão é que o código antigo pode saber apenas como lidar com as 2 enumerações que você possui. Se você adicionar um terceiro valor, Enum.IsDefined será verdadeiro, mas o código antigo não pode necessariamente lidar com isso. Ops.
Há ainda mais diversão que você pode fazer com
[Flags]
enumerações, e o código de validação para isso é um pouco diferente.Também observarei que, para portabilidade, você deve usar call
ToString()
no enum e usá-loEnum.Parse()
ao lê-los novamente. AmbosToString()
eEnum.Parse()
podem lidar com[Flags]
enum é assim, então não há nenhuma razão para não usá-los. Veja bem, é mais uma armadilha, porque agora você não pode nem mudar o nome da enum sem possivelmente quebrar o código.Então, às vezes você precisa ponderar tudo isso acima quando se perguntar: Posso fugir com apenas um bool?
fonte
IMHO parece que um enum seria a escolha óbvia para qualquer situação em que mais de duas opções sejam possíveis. Mas definitivamente existem situações em que um booleano é tudo que você precisa. Nesse caso, eu diria que usar um enum em que um bool funcionaria seria um exemplo de uso de 7 palavras quando 4 funcionarão.
fonte
Os booleanos fazem sentido quando você tem uma alternância óbvia que pode ser apenas uma de duas coisas (ou seja, o estado de uma lâmpada, ligada ou desligada). Fora isso, é bom escrevê-lo de tal maneira que seja óbvio o que você está passando - por exemplo, gravações em disco - sem buffer, com buffer de linha ou síncrono - deve ser transmitido como tal. Mesmo que você não queira permitir gravações síncronas agora (e, portanto, esteja limitado a duas opções), vale a pena considerar torná-las mais detalhadas para saber o que elas fazem à primeira vista.
Dito isto, você também pode usar Falso e Verdadeiro (booleano 0 e 1) e, se precisar de mais valores posteriormente, expanda a função para oferecer suporte a valores definidos pelo usuário (por exemplo, 2 e 3) e seus antigos valores 0/1 será portado muito bem, então seu código não deve ser quebrado.
fonte
Às vezes, é mais simples modelar comportamentos diferentes com sobrecargas. Para continuar do seu exemplo seria:
Essa abordagem é degradante se você tiver vários parâmetros, cada um permitindo um conjunto fixo de opções. Por exemplo, um método que abre um arquivo pode ter várias permutações do modo de arquivo (abrir / criar), acesso ao arquivo (leitura / gravação), modo de compartilhamento (nenhum / leitura / gravação). O número total de configurações é igual aos produtos cartesianos das opções individuais. Naturalmente, nesses casos, várias sobrecargas não são apropriadas.
Enums podem, em alguns casos, tornar o código mais legível, embora validar o valor exato de enum em alguns idiomas (C # por exemplo) possa ser difícil.
Geralmente, um parâmetro booleano é anexado à lista de parâmetros como uma nova sobrecarga. Um exemplo no .NET é:
A última sobrecarga ficou disponível em uma versão posterior da estrutura .NET que a primeira.
Se você sabe que sempre haverá duas opções, um booleano pode ser bom. As enumerações são extensíveis de maneira a não quebrar o código antigo, embora as bibliotecas antigas possam não suportar novos valores de enumeração, portanto o controle de versão não pode ser completamente desconsiderado.
EDITAR
Nas versões mais recentes do C #, é possível usar argumentos nomeados que, como IMO, podem tornar o código de chamada mais claro da mesma maneira que as enumerações. Usando o mesmo exemplo acima:
fonte
Onde eu concordo que as enums são um bom caminho a seguir, nos métodos em que você tem 2 opções (e apenas duas opções, você pode ter legibilidade sem enum.)
por exemplo
Ame os Enums, mas o booleano também é útil.
fonte
Esta é uma entrada tardia em um post antigo, e é tão longe na página que ninguém nunca o lerá, mas como ninguém já o disse ...
Um comentário embutido ajuda bastante a solucionar o
bool
problema inesperado . O exemplo original é particularmente hediondo: imagine tentar nomear a variável na função declearation! Seria algo comoMas, por uma questão de exemplo, digamos que é a declaração. Então, para um argumento booleano inexplicável, coloquei o nome da variável em um comentário embutido. Comparar
com
fonte
Realmente depende da natureza exata do argumento. Se não for sim / não ou verdadeiro / falso, um enum torna-o mais legível. Mas com uma enumeração, você precisa verificar o argumento ou ter um comportamento padrão aceitável, pois valores indefinidos do tipo subjacente podem ser transmitidos.
fonte
O uso de enumerações em vez de booleanos no seu exemplo ajuda a tornar a chamada do método mais legível. No entanto, este é um substituto para o meu item de desejo favorito em C #, denominado argumentos em chamadas de método. Esta sintaxe:
seria perfeitamente legível e, em seguida, você poderia fazer o que um programador deve fazer, que é escolher o tipo mais apropriado para cada parâmetro no método, independentemente da aparência no IDE.
O C # 3.0 permite argumentos nomeados em construtores. Não sei por que eles não podem fazer isso com métodos também.
fonte
Valores booleanos
true
/false
somente. Portanto, não está claro o que representa.Enum
pode ter nome significativo, por exemploOVERWRITE
,APPEND
, etc Então, enums são melhores.fonte