Pergunta

Um colega meu afirma que booleans como método argumentos não são aceitáveis ??. Eles são substituídos por enumerações. No começo eu 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 eu consegui! ;-)
Este é definitivamente um exemplo onde uma enumeração como segundo parâmetro torna o código muito mais legível.

Então, qual é a sua opinião sobre este assunto?

Foi útil?

Solução

booleana é representam "sim / não" escolhas. Se você deseja representar um "sim / não", então use um boolean, deve ser auto-explicativo.

Mas se é uma escolha entre duas opções, nenhuma das quais é claramente sim ou não, em seguida, uma enumeração pode às vezes ser mais legível.

Outras dicas

Enums também permitem modificações futuras, onde agora querem uma terceira escolha (ou mais).

Use o que melhor modelos de seu problema. No exemplo que você dá, a enumeração é uma escolha melhor. No entanto, haveria outras vezes, quando um boolean é melhor. O que faz mais sentido para você:

lock.setIsLocked(True);

ou

enum LockState { Locked, Unlocked };
lock.setLockState(Locked);

Neste caso, eu poderia escolher a opção boolean desde que eu acho que é bastante clara e inequívoca, e estou certo de que meu bloqueio não vai ter mais do que dois estados. Ainda assim, a segunda opção é válida, mas desnecessariamente complicado, IMHO.

Eu acho que você quase respondeu isso sozinho, eu acho que o objetivo final é tornar o código mais legível, e neste caso o enum fez isso, IMO é sempre melhor olhar para o final objectivo em vez de regras gerais, talvez pense nisso mais como uma diretriz ou seja enums são muitas vezes mais legível no código de bools genéricos, ints etc, mas sempre haverá exceções à regra.

Lembre-se a questão Adlai Stevenson posou para embaixador Zorin na ONU durante a dos mísseis de Cuba crise ?

"Você está no tribunal de mundo opinião agora, e você pode responder sim ou não . Você negou que [os mísseis] existem, e eu quero saber se eu Tem entendi bem .... eu sou preparado para esperar pela minha resposta até o inferno congelar, se esse é o seu decisão ".

Se a bandeira que você tem em seu método é de tal natureza que você pode fixá-lo a um decisão binária , e essa decisão será não se transformar em um período de três -way ou n-way decisão, ir para boolean. Indicações: sua bandeira é chamado isXXX .

Não fazê-lo booleano, em caso de algo que é um interruptor modo . Há sempre mais um modo que você pensou ao escrever o método em primeiro lugar.

O dilema-mais-modo uma tem, por exemplo, assombrada Unix, onde os possíveis modos de permissão de um arquivo ou diretório pode ter resultado de hoje em duplos sentidos estranhas de modos, dependendo do tipo de arquivo, a propriedade etc.

Para mim, nem usando boolean nem enumeração é uma boa abordagem. Robert C. Martin capta isso muito claramente em seu Limpo Código Dica # 12: Elimine Argumentos booleanas :

argumentos booleanos alto declarar que a função faz mais de uma coisa. Eles estão confundindo e deve ser eliminado.

Se um método faz mais do que uma coisa, você deveria escrever dois métodos diferentes, por exemplo, no seu caso:. file.append(data) e file.overwrite(data)

Usando uma enumeração não faz as coisas mais claras. Isso não muda nada, ainda é um argumento bandeira.

Há duas razões que eu tenho que correr para este ser uma coisa ruim:

  1. Porque algumas pessoas vão escrever métodos como:

    ProcessBatch(true, false, false, true, false, false, true);
    

    Esta é obviamente ruim, porque é muito fácil de misturar-se parâmetros, e você não tem idéia de olhar para ele o que você está especificando. Apenas um bool não é mau embora.

  2. Como controlar o fluxo de programa por um simples sim / não ramo pode significar que você tem duas funções completamente diferentes que estão envolvidos acima em um de forma awkard. Por exemplo:

    public void Write(bool toOptical);
    

    Realmente, este deve ser de dois métodos

    public void WriteOptical();
    public void WriteMagnetic();
    

    porque o código nestes pode ser totalmente diferente; eles poderiam ter de fazer todos os tipos de tratamento de erros diferente e validação, ou talvez mesmo ter de formatar os dados de saída de forma diferente. Você não pode dizer que apenas usando Write() ou mesmo Write(Enum.Optical) (embora é claro que você pode ter qualquer um desses métodos apenas chamar métodos internos WriteOptical / Mag se você quiser).

Eu acho que só depende. Eu não faria muito grande de um acordo sobre isso, exceto para # 1.

Enums são melhores, mas eu não chamaria params boolean como "inaceitável". Às vezes é apenas mais fácil jogar um pouco boolean e seguir em frente (acho métodos privados etc.)

Booleanos pode ser OK em idiomas que têm parâmetros nomeados, como Python e Objective-C, uma vez que o nome pode explicar o que o parâmetro faz:

file.writeData(data, overwrite=true)

ou

[file writeData:data overwrite:YES]

Eu não concordo que é uma boa regra . Obviamente, Enum faz para um código melhor explícita ou detalhado em alguns casos, mas como regra parece maneira mais abrangente.

Primeiro deixe-me tomar o seu exemplo: A responsabilidade programadores (e capacidade) para escrever um bom código não está realmente comprometida por ter um parâmetro booleano. No seu exemplo o programador poderia ter escrito tão detalhado código escrevendo:

dim append as boolean = true
file.writeData( data, append );

ou eu prefiro mais geral

dim shouldAppend as boolean = true
file.writeData( data, shouldAppend );

Em segundo lugar: O exemplo Enum lhe deu só é "melhor" porque você está passando um CONST. O mais provável na maioria aplicação, pelo menos alguns, se não a maioria dos parâmetros de tempo que são passados ??para funções são variáveis. no caso o meu segundo exemplo (dando variáveis ??com bons nomes), que é muito melhor e Enum teria lhe dado poucos benefícios.

Enums tem um benefício definido, mas você should't basta ir substituindo todos os seus booleans com enums. Há 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 cavar em coisas que eles deveriam fazer, como eles deixá-lo ver o que o true / false realmente significa

Propriedades (especialmente com C # 3 inicializadores de objeto) ou argumentos nomeados (a la Ruby ou Python) são uma maneira muito melhor para ir onde você gostaria de outra forma usar um argumento booleano.

C # exemplo:

var worker = new BackgroundWorker { WorkerReportsProgress = true };

Rubi exemplo

validates_presence_of :name, :allow_nil => true

Python exemplo

connect_to_database( persistent=true )

A única coisa que eu posso pensar de onde um argumento de método boolean é a coisa certa a fazer é em Java, onde você não tem tanto propriedades ou argumentos de palavra-chave. Esta é uma das razões que eu odeio java: - (

Embora seja verdade que, em muitos casos enums são mais legíveis e mais extensível que booleans, uma regra absoluta que "booleans não são aceitáveis" é um disparate. É inflexível e contra-produtivo - não deixar espaço para o julgamento humano. Eles são um elemento fundamental construído em tipo na maioria das línguas, porque eles são úteis - considere aplicá-lo a outros built-in-tipos: dizendo, por exemplo, "nunca use um int como parâmetro" seria apenas louco

.

Esta regra é apenas uma questão de estilo, não da possibilidade de erros ou desempenho de tempo de execução. Uma regra melhor seria "preferem enums para booleans por razões de legibilidade".

Olhe para o framework .Net. Booleans são usados ??como parâmetros em alguns métodos. O .Net API não é perfeito, mas eu não acho que o uso de boolean como parâmetros é um grande problema. A dica sempre lhe dá o nome do parâmetro, e você pode construir este tipo de orientação também -. Preenchimento em seus comentários XML sobre os parâmetros do método, eles vão vir para cima na dica

Gostaria também de acrescentar que há um caso em que você deve booleans claramente refactor para uma enumeração - quando você tem dois ou mais booleans em sua classe, ou em seus parâmetros de método, e não todos os estados são válidos (por exemplo, não é válida ter os dois set true).

Por exemplo, se sua classe tem propriedades como

public bool IsFoo
public bool IsBar

E é um erro ter ambos verdade, ao mesmo tempo, o que você realmente tem é três estados válidos, melhor expressas como algo como:

enum FooBarType { IsFoo, IsBar, IsNeither };

Algumas regras que o seu colega pode ser mais bem aderentes são:

  • Do not ser dogmático com seu projeto.
  • Escolha o que se encaixa de forma mais apropriada para os usuários do seu código.
  • Não tente festança estacas em forma de estrela em cada buraco apenas porque você gosta da forma este mês!

Um valor booleano só seria aceitável se você não pretende estender a funcionalidade do quadro. O Enum é preferido porque você pode estender a enumeração e não quebrar implementações anteriores da chamada de função.

A outra vantagem do Enum é que é mais fácil de ler.

Se o método faz uma pergunta como:

KeepWritingData (DataAvailable());

onde

bool DataAvailable()
{
    return true; //data is ALWAYS available!
}

void KeepWritingData (bool keepGoing)
{
   if (keepGoing)
   {
       ...
   }
}

boolean argumentos de método parece fazer sentido absolutamente perfeito.

Depende do método. Se o método faz algo que é muito, obviamente, uma coisa verdadeira / falsa então é bom, por exemplo, abaixo [embora não eu não estou dizendo que este é o melhor projeto para este método, é apenas um exemplo de onde o uso é óbvio].

CommentService.SetApprovalStatus(commentId, false);

No entanto, na maioria dos casos, como o exemplo que você menciona, é melhor usar uma enumeração. Há muitos exemplos no próprio .NET Framework onde esta convenção não é seguido, mas isso é porque eles introduziram essa diretriz de design bastante tarde no ciclo.

Ele faz as coisas um pouco mais explícitas, mas não começar a estender enormemente a complexidade das suas interfaces - em uma escolha boolean pura como anexando / substituí-lo parece um exagero. Se você precisar adicionar uma outra opção (o que eu não consigo pensar em neste caso), você sempre pode executar uma refactor (dependendo do idioma)

Enums certamente pode tornar o código mais legível. Há ainda algumas coisas para prestar atenção para fora (em .net, pelo menos)

Como o armazenamento subjacente de uma enumeração é um int, o valor padrão será zero, então você deve se certificar de que 0 é um padrão razoável. (Estruturas Ex ter todos os campos ajustado para zero quando criado, então não há nenhuma maneira de especificar um padrão diferente de 0. Se você não tem um valor 0, você não pode mesmo testar a enumeração sem lançar para int, o que seria mau estilo.)

Se o seu de enum são particulares ao seu código (nunca expostos publicamente), então você pode parar de ler aqui.

Se o seu enums são publicada em qualquer forma de código externo e / ou são guardados fora do programa, considere numeração-los explicitamente. O compilador automaticamente números de los de 0, mas se você reorganizar suas enums sem dar-lhes valores que você pode acabar com defeitos.

Eu posso legalmente escrever

WriteMode illegalButWorks = (WriteMode)1000000;
file.Write( data, illegalButWorks );

Para combater isso, qualquer código que consome uma enumeração que você não pode ter certeza de (por exemplo API pública) precisa verificar se a enumeração é válido. Você faz isso através

if (!Enum.IsDefined(typeof(WriteMode), userValue))
    throw new ArgumentException("userValue");

A única ressalva de Enum.IsDefined é que ele usa reflexão e é mais lento. Ele também sofre uma questão de versões. Se você precisa verificar o valor enum frequentemente, você seria melhor fora o seguinte:

public static bool CheckWriteModeEnumValue(WriteMode writeMode)
{
  switch( writeMode )
  {
    case WriteMode.Append:
    case WriteMode.OverWrite:
      break;
    default:
      Debug.Assert(false, "The WriteMode '" + writeMode + "' is not valid.");
      return false;
  }
  return true;
}

A questão de versão é que o código de idade só podem saber como lidar com os 2 enums você tem. Se você adicionar um terceiro valor, Enum.IsDefined vai ser verdade, mas o código antigo não pode necessariamente lidar com isso. Whoops.

Há ainda mais divertido você pode fazer com enums [Flags], eo código de validação para que seja ligeiramente diferente.

Eu também vai notar que para a portabilidade, você deve usar ToString() chamada no enum, e uso Enum.Parse() quando lê-los de volta. Ambos ToString() e Enum.Parse() pode lidar com [Flags] enum é assim, então não há nenhuma razão para não usá-los. Mente-lhe, é ainda outra armadilha, porque agora você não pode mesmo mudar o nome da enumeração sem código possivelmente quebra.

Então, às vezes você precisa pesar todos os itens acima, em que se pergunta Posso obter afastado com apenas um bool?

IMHO parece uma enumeração seria a escolha óbvia para qualquer situação onde mais de duas opções são possíveis. Mas há definitivamente as situações onde um booleano é tudo que você precisa. Nesse caso, eu diria que o uso de uma enumeração onde um bool iria trabalhar seria um exemplo de utilização 7 palavras quando 4 vai fazer.

Booleans faz sentido quando você tem uma alternância óbvio que só pode ser uma de duas coisas (ou seja, o estado de uma lâmpada, ligado ou desligado). Fora isso, é bom para escrevê-lo de tal forma a que seja óbvio o que você está passando - por exemplo, gravações de disco - não tamponada, linha-tamponadas, ou síncronos - deve ser passado como tal. Mesmo se você não quiser permitir gravações síncronas agora (e assim você está limitado a duas opções), vale a pena considerar o que os torna mais detalhado, para efeitos de saber o que eles fazem à primeira vista.

Dito isso, você também pode usar False e True (booleano 0 e 1) e, em seguida, se você precisar de mais valores posteriormente, expandir a função para valores definidos pelo usuário de apoio (digamos, 2 e 3), e seu velho 0 / 1 valores transportaremos bem, para que o seu código não deve quebrar.

Às vezes é apenas mais simples de modelar o comportamento diferente com sobrecargas. Para continuar com seu exemplo seria:

file.appendData( data );  
file.overwriteData( data );

Esta abordagem degrada Se você tiver vários parâmetros, cada um permitindo que um conjunto fixo de opções. Por exemplo, um método que abre um arquivo pode ter várias permutações de modo de arquivo (abertura / criar), o acesso ao arquivo (leitura / gravação), a partilha de modo (none / 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 adequadas.

Enums podem, em alguns casos, tornar o código mais legível, embora validar o valor de enumeração exata em algumas línguas (C #, por exemplo) pode ser difícil.

Muitas vezes, um parâmetro booleano é anexado à lista de parâmetros como uma nova sobrecarga. Um exemplo em .NET é:

Enum.Parse(str);  
Enum.Parse(str, true); // ignore case

O último sobrecarga tornou-se disponível em uma versão posterior do .NET framework que o primeiro.

Se você sabe que há só vai ser duas opções, um booleano pode ser bom. Enums são extensíveis de uma forma que não vai quebrar o código antigo, embora bibliotecas antigas pode não suportar novos valores enum assim versionamento não pode ser completamente descartada.


Editar

Em versões mais recentes de C # é possível usar argumentos nomeados que, IMO, pode fazer chamando código mais claro da mesma forma que enums pode. Usando o mesmo exemplo acima:

Enum.Parse(str, ignoreCase: true);

Onde eu concordo que Enums são bom caminho a percorrer, em métodos de onde você tem 2 opções (e apenas duas opções que você pode ter a legibilidade sem enum).

por exemplo.

public void writeData(Stream data, boolean is_overwrite)

Amor as enumerações, mas boolean é útil também.

Esta é uma entrada tardia em um post antigo, e é tão longe para baixo a página que ninguém vai lê-lo, mas como ninguém disse que já ....

Comentário Uma linha vai um longo caminho para resolver o problema bool inesperado. O exemplo original é particularmente odiosa: imagine tentar nomear a variável no declearation função! Seria algo como

void writeData( DataObject data, bool use_append_mode );

Mas, por uma questão de exemplo, vamos dizer que é a declaração. Então, para um argumento booleano de outra forma inexplicável, eu coloquei o nome da variável em um comentário em linha. Compare

file.writeData( data, true );

com

file.writeData( data, true /* use_append_mode */);

É realmente depende da natureza exata do argumento. Se não é um sim / não ou verdadeiro / falso, em seguida, uma enumeração torna-lo mais legível. Mas com uma enumeração, você precisa verificar o argumento ou ter comportamento padrão aceitável desde valores indefinidos do tipo subjacente pode ser passado.

O uso de enums em vez de booleans no seu exemplo que ajuda a fazer a chamada de método mais legível. No entanto, este é um substituto para o meu item de desejo favorito em C #, argumentos nomeados em chamadas de método. Esta sintaxe:

var v = CallMethod(pData = data, pFileMode = WriteMode, pIsDirty = true);

seria perfeitamente legível, e você poderia, então, fazer o que um programador deve fazer, que é escolher o tipo mais adequado para cada parâmetro no método sem levar em conta como ele olha no IDE.

C # 3.0 permite argumentos nomeados em construtores. Eu não sei por que eles não podem fazer isso com métodos bem.

Booleans valores somente true / false. Portanto, não está claro o que ele representa. Enum pode ter nome significativo, por exemplo OVERWRITE, APPEND, etc. Então enums são melhores.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top