Manutenção da lógica booleana - É o aninhamento se forem necessárias instruções?

11

Qual destes é melhor para manutenção?

if (byteArrayVariable != null)
    if (byteArrayVariable .Length != 0)
         //Do something with byteArrayVariable 

OU

if ((byteArrayVariable != null) && (byteArrayVariable.Length != 0))
  //Do something with byteArrayVariable 

Prefiro ler e escrever o segundo, mas lembro-me de ler em código completo que fazer coisas assim é ruim para a manutenção.

Isso ocorre porque você está confiando no idioma para não avaliar a segunda parte do ifse a primeira parte for falsa e nem todos os idiomas fazem isso. (A segunda parte lançará uma exceção se avaliada com um nulo byteArrayVariable.)

Não sei se isso é realmente algo para se preocupar ou não, e gostaria de obter um feedback geral sobre a questão.

Obrigado.

Vaccano
fonte
1
A primeira forma tem um risco de de oscilação-else
JBRWilkinson
apenas curioso, com qual idioma você está trabalhando?
STW
@STW - usamos C # e temos algum código legado no Delphi.
Vaccano 27/10/10
2
bom saber. Não conheço o Delphi, mas em C # você pode confiar nos operadores &&e na ||avaliação de curto-circuito. Indo de um idioma para outro geralmente tem algumas desvantagens, e é bom ser firme ao fazer revisões de código para ajudar a capturar esses pequenos problemas.
STW
Embora, juntamente com muitas respostas, eu ache a segunda mais legível, a primeira é muito mais fácil de depurar. Nesse caso, eu ainda usaria o segundo porque você está verificando as coisas fáceis de depurar. Mas tenha cuidado em geral.
Magus

Respostas:

30

Eu acho que a segunda forma é boa e também representa mais claramente o que você está tentando fazer. Você diz que...

Lembro-me de ler no código completo que fazer coisas assim é ruim para a manutenção. Isso ocorre porque você está confiando no idioma para não avaliar a segunda parte do if se a primeira parte for falsa e nem todos os idiomas o fizerem.

Não importa se todos os idiomas fazem isso. Você está escrevendo em um idioma específico, então só importa se esse idioma fizer isso. Caso contrário, você está basicamente dizendo que não deve usar os recursos de um idioma específico, porque outros idiomas podem não suportar esses recursos, e isso é bobagem.

mipadi
fonte
12
Acordado. Por que diabos eu me importaria se meu código funcionaria da mesma maneira quando copiado para outro idioma?
Tim Goodman
16

Estou surpreso que ninguém tenha mencionado isso (por isso espero não estar interpretando mal a pergunta) - mas ambos são péssimos!

A melhor alternativa seria usar as saídas antecipadas (colocando uma declaração de retorno no início do código, se nenhum outro código for executado). Isso pressupõe que seu código seja relativamente bem refatorado e que cada método tenha um objetivo conciso.

Nesse ponto, você faria algo como:

if ((byteArrayVariable == null) || (byteArrayVariable.Length != 0)) {
  return; // nothing to be done, leaving
}

// Conditions met, do something

Pode parecer muito com validação - e é exatamente isso que é. Ele valida que as condições foram satisfeitas para o método - as duas opções que você ofereceu ocultaram a lógica real nas verificações de pré-condição.

Se o seu código é muito espaguete (métodos loooong, muitos ifs aninhados com lógica dispersa entre eles etc.), você deve se concentrar no problema maior de colocar seu código em forma antes dos problemas estilísticos menores. Dependendo do seu ambiente e da gravidade da bagunça, existem algumas ótimas ferramentas para ajudar (por exemplo, o Visual Studio possui uma função de refatoração "Extrair método").


Como Jim menciona, ainda há espaço para debate sobre como estruturar a saída antecipada. A alternativa ao que está acima seria:

if (byteArrayVariable == null) 
  return; // nothing to be done, leaving

if (byteArrayVariable.Length != 0)
  return;

// Conditions met, do something
STW
fonte
1
Eu concordo com o retorno, em vez da quebra da boa lógica, mas as mesmas pessoas usarão o mesmo argumento sobre o uso de || quanto a &&.
MIA
14

Não perca tempo tentando capturar todas essas condições. Se você pode desqualificar os dados ou condições, faça-o assim que possível.

if (byteArrayVariable == null) Exit;
if (byteArrayVariable.Length == 0) Exit;
// do something

Isso permite que você adapte seu código muito mais facilmente para novas condições

if (byteArrayVariable == null) Exit;
if (byteArrayVariable.Length == 0) Exit;
if (NewCondition == false) Exit;
if (NewerCondition == true) Exit;
// do something
Michael Riley - também conhecido por Gunny
fonte
2
+1 Sim, sim, sim. Lógica tão simples quanto isso não deve resultar em código difícil. Seu intestino (do solicitante) deve estar lhe dizendo isso.
Job
11

Seu exemplo é o exemplo perfeito de por que os aninhados ifssão uma abordagem ruim para a maioria dos idiomas que oferecem suporte adequado à comparação booleana. Aninhar ifscria uma situação em que sua intenção não é clara. É necessário que o segundo ifsiga imediatamente o primeiro? Ou é apenas porque você ainda não colocou outra operação lá?

if ((byteArrayVariable != null) && (byteArrayVariable.Length != 0))
  //Do something with byteArrayVariable 

Nesse caso, esses praticamente devem estar juntos. Eles estão agindo como um guarda para garantir que você não execute uma operação que resultaria em uma exceção. Usando aninhado ifs, você deixa para a interpretação da pessoa que o segue se os dois representam uma guarda de duas partes ou alguma outra lógica de ramificação. Ao combiná-los em um único if, estou afirmando isso. É muito mais difícil esgueirar-se para dentro de uma operação onde não deveria estar.

Eu sempre vou favorecer a comunicação clara da minha intenção sobre a afirmação estúpida de alguém de que "não funciona em todas as línguas". Adivinha o que ... throwtambém não funciona em todas as linguagens, isso significa que eu não devo usá-lo ao codificar em C # ou Java e, em vez disso, devo confiar nos valores de retorno para sinalizar quando houve um problema?

Tudo isso dito, é muito mais legível invertê-lo e retornar ao método se um deles não estiver satisfeito, como diz o STW em sua resposta.

MIA
fonte
Mais de duas condições podem justificar sua própria função que retorna um nulo.
Job
3

Nesse caso, suas duas cláusulas estão diretamente relacionadas, portanto, a segunda forma é preferível.

Se eles não fossem relacionados (por exemplo, uma série de cláusulas de guarda em condições diferentes), eu preferiria a 1ª forma (ou refataria um método com um nome que represente o que a condição composta realmente representa).

FinnNk
fonte
1

Se você trabalha em Delphi, a primeira maneira é, em geral, mais segura. (Para o exemplo dado, não há muito a ganhar com o uso de ifs aninhados.)

O Delphi permite que você especifique se as expressões booleanas devem ou não ser avaliadas ou "curto-circuitadas" através de opções do compilador. A maneira # 1 (ifs aninhados) permite garantir que a segunda cláusula execute se e somente se (e estritamente depois ) a primeira cláusula for avaliada como verdadeira.

Frank Shearar
fonte
1
Nos 23 anos de desenvolvimento do Delphi, nunca mudei a opção "Complete boolean eval". Se eu estivesse enviando código para outro lugar, colocaria {$ BOOLEVAL OFF} ou {$ B-} na unidade.
Gerry
Nem eu. Também sempre uso a verificação de alcance também ... mas outras pessoas não. E isso não deve fazer diferença, exceto, talvez, do ponto de vista de desempenho, porque você não deve usar efeitos colaterais em funções usadas em expressões booleanas. Tenho certeza que alguém lá fora, no entanto!
Frank Shearar
@ Greg: De acordo. A única razão pela qual você pode desejar uma avaliação completa é os efeitos colaterais - e efeitos colaterais condicionais são uma má idéia!
Loren Pechtel
Basta reler o meu comentário 23 anos deve ser 13! Eu não tenho um Tardis
Gerry
1

O segundo estilo pode sair pela culatra no VBA, pois se o primeiro teste for se o objeto for gerado, ele ainda fará o segundo teste e obterá um erro. Acabei de adquirir o hábito no VBA ao lidar com a verificação de objetos para sempre usar o primeiro método.


fonte
2
Isso porque o VBA é uma linguagem terrível
Falmarri 19/10/10
@Falmarri, tem algumas coisas terríveis (e algumas boas). Eu consertaria um monte de coisas se tivesse meus manobristas.
2
A falta de avaliação booleana de curto-circuito é mais um legado do que qualquer coisa. Não faço ideia por que eles não começaram com isso. A "correção" do VB.NET de OrElse e AndAlso é meio chata, mas provavelmente a única opção para lidar com isso sem quebrar a compatibilidade com versões anteriores.
JohnFx
1

O segundo formulário é mais legível, especialmente se você estiver usando blocos {} em torno de cada cláusula, porque o primeiro formulário levará a {} blocos aninhados e a vários níveis de indentação, o que pode ser difícil de ler (você precisa contar espaços de indentação) para descobrir onde cada bloco termina).

o. nate
fonte
0

Acho que ambos são legíveis e não aceito o argumento de que você deve se preocupar com a manutenção do código se quiser trocar de idioma.

Em alguns idiomas, a segunda opção tem um desempenho melhor, portanto seria a escolha mais clara.

Conta
fonte
0

Estou contigo; Eu faço da segunda maneira. Para mim, é logicamente mais claro. A preocupação de que algumas linguagens executem a segunda parte, mesmo que a primeira seja avaliada como falsa, parece um pouco tola para mim, pois nunca na minha vida escrevi um programa executado em "algumas linguagens"; meu código sempre parece existir em um idioma específico, que pode lidar com essa construção ou não. (E se eu não tiver certeza se o idioma específico pode lidar com isso, é algo que realmente preciso aprender, não é?)

Na minha opinião, o perigo da primeira maneira de fazer isso é que me deixa vulnerável a inserir acidentalmente código fora desse ifbloco aninhado quando não pretendia. O que, reconhecidamente, dificilmente é uma grande preocupação, mas parece mais razoável do que se preocupar se meu código é independente de idioma.

BlairHippo
fonte
0

Eu prefiro a segunda opção, porque é mais legível.

Se a lógica que você está implementando for mais complexa (verificar se uma sequência não é nula e não está vazia é apenas um exemplo), você pode fazer uma refatoração útil: extrair uma função booleana. Estou com @mipadi na observação "Code Complete" ("não importa se todos os idiomas fazem isso ...") Se o leitor do seu código não estiver interessado em procurar dentro da função extraída, a ordem em que operandos booleanos são avaliados torna-se uma questão discutível para eles.

azheglov
fonte
0
if ((byteArrayVariable != null)
 && (byteArrayVariable.Length != 0)) {
    //Do something with byteArrayVariable 

mas essa é basicamente a segunda opção.

Boicote SE para Monica Cellio
fonte