Aqui está um exemplo muito simplificado . Esta não é necessariamente uma pergunta específica do idioma, e peço que você ignore as muitas outras maneiras pelas quais a função pode ser escrita e as alterações que podem ser feitas nela. . A cor é de um tipo único
string CanLeaveWithoutUmbrella()
{
if(sky.Color.Equals(Color.Blue))
{
return "Yes you can";
}
else
{
return "No you can't";
}
}
Muitas pessoas que eu conheci, ReSharper, e esse cara (cujo comentário me lembrou que eu estava procurando isso por um tempo) recomendariam refatorar o código para remover o else
bloco deixando isso:
(Não me lembro do que a maioria disse, eu poderia não ter perguntado isso de outra forma)
string CanLeaveWithoutUmbrella()
{
if(sky.Color.Equals(Color.Blue))
{
return "Yes you can";
}
return "No you can't";
}
Pergunta: Existe um aumento na complexidade introduzido por não incluir o else
bloco?
Estou com a impressão de que a else
intenção mais diretamente indica, afirmando o fato de que o código nos dois blocos está diretamente relacionado.
Além disso, acho que posso evitar erros sutis na lógica, especialmente após modificações no código posteriormente.
Pegue esta variação do meu exemplo simplificado (ignorando o fato do or
operador, pois este é um exemplo propositadamente simplificado):
bool CanLeaveWithoutUmbrella()
{
if(sky.Color != Color.Blue)
{
return false;
}
return true;
}
Agora, alguém pode adicionar um novo if
bloco com base em uma condição após o primeiro exemplo, sem imediatamente reconhecer corretamente que a primeira condição está colocando uma restrição em sua própria condição.
Se um else
bloco estivesse presente, quem adicionasse a nova condição seria forçado a mover o conteúdo do else
bloco (e se, de alguma forma, passar por cima dele, a heurística mostrará que o código é inacessível, o que não ocorre no caso de um if
restringir o outro) .
É claro que existem outras maneiras pelas quais o exemplo específico deve ser definido de qualquer maneira, todos os que impedem essa situação, mas é apenas um exemplo.
O comprimento do exemplo que eu dei pode distorcer o aspecto visual disso; portanto, suponha que o espaço ocupado entre parênteses seja relativamente insignificante para o restante do método.
Esqueci de mencionar um caso em que concordo com a omissão de um bloco else e é quando o uso é usado if
para aplicar uma restrição que deve ser logicamente satisfeita para todo o código a seguir, como uma verificação nula (ou qualquer outra proteção) .
fonte
else
cláusula (a meu gosto, será ainda mais legível ao deixar de fora os colchetes desnecessários. Mas acho que o exemplo não é bem escolhido, porque no caso acima eu escreveriareturn sky.Color == Color.Blue
(sem if / else) Um exemplo com um tipo de retorno diferente do que bool provavelmente tornaria isso mais claro.Respostas:
Na minha opinião, o bloco else explícito é preferível. Quando eu vejo isso:
Eu sei que estou lidando com opções mutuamente exclusivas. Não preciso ler o que há dentro dos blocos if para saber.
Quando eu vejo isso:
Parece, à primeira vista, que ele retorna falso independentemente e tem um efeito colateral opcional: o céu não é azul.
A meu ver, a estrutura do segundo código não reflete o que o código realmente faz. Isso é ruim. Em vez disso, escolha a primeira opção que reflete a estrutura lógica. Não quero verificar a lógica de retorno / interrupção / continuação em cada bloco para conhecer o fluxo lógico.
Por que alguém prefere o outro é um mistério para mim, espero que alguém tome a posição oposta e me esclareça com a resposta.
Eu diria que as condições de guarda são boas no caso de você ter deixado o caminho feliz. Ocorreu um erro ou falha que impede a execução comum de continuar. Quando isso acontece, você deve lançar uma exceção, retornar um código de erro ou o que for apropriado para o seu idioma.
Logo após a versão original desta resposta, código como este foi descoberto no meu projeto:
Por não colocar o retorno dentro de outras partes, o fato de que faltava uma declaração de retorno foi ignorado. Se mais tivesse sido empregado, o código nem teria sido compilado. (Obviamente, a preocupação muito maior que tenho é que os testes escritos neste código foram muito ruins para não detectar esse problema.)
fonte
else
bloco (pode ser um incômodo quando sou forçado a fazê-lo apenas para me manter alinhado com as convenções de uma base de código). Também estou interessado em ver o contraponto aqui.} // else
onde o outro normalmente iria como um compromisso entre os dois.A principal razão para remover o
else
bloco que encontrei é o recuo excessivo. O curto-circuito doselse
blocos permite uma estrutura de código muito mais plana.Muitas
if
declarações são essencialmente guardas. Eles estão impedindo o cancelamento de referência de ponteiros nulos e outros "não faça isso!" erros. E eles levam ao término rápido da função atual. Por exemplo:Agora pode parecer que uma
else
cláusula seria muito razoável aqui:E, de fato, se é isso que você está fazendo, não é muito mais recuado que o exemplo acima. Mas isso raramente é o que você faz com estruturas de dados reais de vários níveis (uma condição que ocorre o tempo todo ao processar formatos comuns como JSON, HTML e XML). Portanto, se você deseja modificar o texto de todos os filhos de um determinado nó HTML, diga:
Os guardas não aumentam o recuo do código. Com uma
else
cláusula, todo o trabalho real começa a se mover para a direita:Isso está começando a ter um movimento significativo para a direita, e este é um exemplo bastante trivial, com apenas duas condições de guarda. Cada guarda adicional adiciona outro nível de recuo. Código real que eu escrevi processando XML ou consumindo feeds JSON detalhados pode facilmente empilhar 3, 4 ou mais condições de guarda. Se você sempre usa o
else
, acaba com um recuo de 4, 5 ou 6 níveis. Talvez mais. E isso definitivamente contribui para um senso de complexidade do código e mais tempo gasto para entender o que está alinhado com o que. O estilo rápido, plano e de saída antecipada elimina parte dessa "estrutura em excesso" e parece mais simples.Adendo Os comentários a esta resposta me fizeram perceber que talvez não estivesse claro que o manuseio NULL / vazio não é o único motivo para condicionais de guarda. Nem perto! Embora este exemplo simples se concentre no manuseio NULL / vazio e não contenha outros motivos, pesquisar estruturas profundas como XML e ASTs em busca de conteúdo "relevante", por exemplo, geralmente possui uma longa série de testes específicos para eliminar nós irrelevantes e desinteressantes casos. Uma série de testes "se este nó não for relevante, retorne" causará o mesmo tipo de desvio à direita que os guardas NULL / vazios causarão. E, na prática, testes de relevância subsequentes são freqüentemente acoplados a proteções NULL / vazias para as estruturas de dados correspondentemente mais profundas pesquisadas - um golpe duplo para desvio à direita.
fonte
else
bloco supérfluo, ao usar umif
bloco para aplicar uma restrição que deve ser logicamente satisfeita para todo o código a seguir, como uma verificação nula (ou qualquer outra proteção).then
cláusula (como declarações de guarda sucessivas), não haverá valor específico para evitar aelse
cláusula em particular . De fato, isso reduziria a clareza e potencialmente seria perigoso (ao não declarar simplesmente a estrutura alternativa que existe / deveria estar lá).Quando eu vejo isso:
Eu vejo um idioma comum . Um padrão comum , que na minha cabeça se traduz em:
Como esse é um idioma muito comum na programação, o programador que está acostumado a ver esse tipo de código tem uma 'tradução' implícita em sua cabeça sempre que o vê, que o 'converte' no significado apropriado.
Não preciso do explícito
else
, minha cabeça 'coloca lá' para mim porque reconheceu o padrão como um todo . Entendo o significado de todo o padrão comum, por isso não preciso de pequenos detalhes para esclarecê-lo.Por exemplo, leia o seguinte texto:
Você leu isso?
Se sim, você está errado. Leia o texto novamente. O que está realmente escrito é
Existem duas palavras "as" no texto. Então, por que você perdeu um dos dois?
É porque seu cérebro viu um padrão comum e o traduziu imediatamente para seu significado conhecido. Não era necessário ler a coisa inteira detalhadamente para descobrir o significado, porque já reconhecia o padrão ao vê-lo. Por isso, ignorou o extra "the ".
A mesma coisa com o idioma acima. Programadores que leram muito código estão acostumados a ver esse padrão , de
if(something) {return x;} return y;
. Eles não precisam de explícitoselse
para entender o significado, porque o cérebro deles 'coloca isso para eles'. Eles o reconhecem como um padrão com um significado conhecido: "o que está depois da chave de fechamento será executado apenas como implícitoelse
no anteriorif
".Então, na minha opinião, o
else
é desnecessário. Mas basta usar o que parece mais legível.fonte
Eles adicionam desorganização visual no código; portanto, podem adicionar complexidade.
No entanto, o oposto também é verdadeiro, a refatoração excessiva para reduzir o comprimento do código também pode adicionar complexidade (não neste caso simples, é claro).
Concordo com a afirmação de que o
else
bloco indica intenção. Mas minha conclusão é diferente, porque você está adicionando complexidade ao seu código para conseguir isso.Não concordo com sua afirmação de que ela permite evitar erros sutis na lógica.
Vamos ver um exemplo, agora ele só deve retornar verdadeiro se o céu for azul e houver alta umidade, a umidade não for alta ou se o céu for vermelho. Mas então, você confunde uma chave e a coloca errada
else
:Vimos todo esse tipo de erro bobo no código de produção e pode ser muito difícil de detectar.
Eu sugeriria o seguinte:
Acho isso mais fácil de ler, porque posso ver rapidamente que o padrão é
false
.Além disso, a idéia de forçar a movimentação do conteúdo de um bloco para inserir uma nova cláusula é suscetível a erros. De alguma forma, isso se relaciona com o princípio aberto / fechado (o código deve estar aberto para extensão, mas fechado para modificação). Você está forçando a modificar o código existente (por exemplo, o codificador pode apresentar um erro no balanceamento de chaves).
Finalmente, você está levando as pessoas a responder de uma maneira predeterminada que você acha que é a certa. Por exemplo, você apresenta um exemplo muito simples, mas depois declara que as pessoas não devem levar isso em consideração. No entanto, a simplicidade do exemplo afeta toda a questão:
Se o caso for tão simples quanto o apresentado, minha resposta seria omitir quaisquer
if else
cláusulas.return (sky.Color == Color.Blue);
Se o caso for um pouco mais complicado, com várias cláusulas, eu responderia:
bool CanLeaveWithoutUmbrella () {
}
Se o caso for complicado e nem todas as cláusulas retornarem verdadeiras (talvez elas retornem um duplo), e eu quero introduzir algum comando de ponto de interrupção ou loggin, consideraria algo como:
}
Se não estamos falando de um método que retorna algo, mas de um bloco if-else que define alguma variável ou chama um método, então sim, incluiria uma
else
cláusula final .Portanto, a simplicidade do exemplo também é um fator a considerar.
fonte
Embora o caso if-else descrito tenha maior complexidade, na maioria das situações práticas (mas não em todas), isso não importa muito.
Anos atrás, quando eu estava trabalhando em um software usado para a indústria da aviação (tinha que passar por um processo de certificação), a sua foi uma pergunta que surgiu. Aconteceu que havia um custo financeiro para essa declaração 'else', pois aumentou a complexidade do código.
Aqui está o porquê.
A presença do 'else' criou um terceiro caso implícito que teve que ser avaliado - aquele do 'if' nem do 'else' sendo tomados. Você pode estar pensando (como eu estava na época) WTF?
A explicação foi assim ...
Do ponto de vista lógico, existem apenas as duas opções - o 'if' e o 'else'. No entanto, o que estávamos certificando era o arquivo de objeto que o compilador estava gerando. Portanto, quando havia um 'mais', era necessário fazer uma análise para confirmar que o código de ramificação gerado estava correto e que um terceiro caminho misterioso não apareceu.
Compare isso com o caso em que existe apenas um 'se' e nenhum 'mais'. Nesse caso, existem apenas duas opções - o caminho 'se' e o caminho 'não-se'. Dois casos a avaliar são menos complexos que três.
Espero que isto ajude.
fonte
O objetivo mais importante do código é ser compreensível como confirmado (em oposição a facilmente refatorado, o que é útil, mas menos importante). Um exemplo de Python um pouco mais complexo pode ser usado para ilustrar isso:
Isso é bastante claro - é o equivalente a uma
case
declaração ou pesquisa de dicionário, a versão de nível superior dereturn foo == bar
:Isso é mais claro, porque, embora o número de tokens seja maior (32 vs 24 para o código original com dois pares de chave / valor), a semântica da função agora é explícita: é apenas uma pesquisa.
(Isso tem consequências para a refatoração - se você quiser ter um efeito colateral
key == NIK
, terá três opções:if
/elif
/else
e insira uma única linha nokey == NIK
estojo.if
instruçãovalue
para o caso especial antes de retornar.if
declaração no chamador.Agora vemos por que a função de pesquisa é uma poderosa simplificação: torna a terceira opção obviamente a solução mais simples, pois além de resultar em uma diferença menor do que a primeira opção , é a única que garante
value
apenas uma coisa. )Voltando ao código do OP, acho que isso pode servir como um guia para a complexidade das
else
instruções: o código com umaelse
declaração explícita é objetivamente mais complexo, mas mais importante é se torna a semântica do código clara e simples. E isso deve ser respondido caso a caso, pois cada parte do código tem um significado diferente.fonte
case
ouswitch
declarações) são menos homogêneos. Várias opções são apenas retornos de valor diretos, mas um ou dois casos precisam de processamento extra. E quando você descobre a estratégia de pesquisa não se adequar, você precisa reescrever em uma totalmente diferenteif
elif
...else
sintaxe.Eu acho que depende do tipo de
if
declaração que você está usando. Algumasif
instruções podem ser vistas como expressões e outras apenas como instruções de fluxo de controle.Seu exemplo parece uma expressão para mim. Nas linguagens do tipo C, o operador ternário
?:
é muito parecido com umaif
declaração, mas é uma expressão e a outra parte não pode ser omitida. Faz sentido que um ramo else não possa ser omitido em uma expressão, porque a expressão sempre deve ter um valor.Usando o operador ternário, seu exemplo seria assim:
Já que é uma expressão booleana, ela realmente deve ser simplificada até o fim
A inclusão de uma cláusula else explícita torna sua intenção mais clara. Os guardas podem fazer sentido em algumas situações, mas ao selecionar entre dois valores possíveis, nenhum dos quais é excepcional ou incomum, eles não ajudam.
fonte
I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Outra coisa a se pensar nesse argumento são os hábitos que cada abordagem promove.
if / else renomeia uma amostra de codificação nos termos de ramificações. Como você diz, às vezes essa explicitação é boa. Realmente existem dois caminhos de execução possíveis e queremos destacar isso.
Em outros casos, há apenas um caminho de execução e todas essas coisas excepcionais com as quais os programadores devem se preocupar. Como você mencionou, as cláusulas de guarda são ótimas para isso.
Existe, é claro, o caso 3 ou mais (n), em que geralmente encorajamos o abandono da cadeia if / else e o uso de uma declaração case / switch ou polimorfismo.
O princípio orientador que tenho em mente aqui é que um método ou função deve ter apenas um propósito. Qualquer código com uma instrução if / else ou switch é apenas para ramificação. Portanto, deve ser absurdamente curto (4 linhas) e delegar apenas ao método correto ou produzir o resultado óbvio (como no seu exemplo). Quando seu código é tão curto, é difícil perder esses retornos múltiplos :) Assim como "goto considerado prejudicial", qualquer declaração de ramificação pode ser abusada; portanto, colocar algum pensamento crítico em outras declarações geralmente vale a pena. Como você mencionou, apenas ter um bloco else fornece um lugar para o código agrupar. Você e sua equipe terão que decidir como você se sente sobre isso.
O que a ferramenta está realmente reclamando é o fato de você ter um retorno / quebra / lançamento dentro do bloco if. No que diz respeito, você escreveu uma cláusula de guarda, mas estragou tudo. Você pode optar por deixar a ferramenta feliz ou "entreter" sua sugestão sem aceitá-la.
fonte
else
semanticamente, as ferramentas de análise de código geralmente procuram instruções estranhas, porque geralmente destacam um mal-entendido sobre o fluxo de controle. Oelse
depois de umif
que retorna é bits desperdiçados.if(1 == 1)
geralmente aciona o mesmo tipo de aviso.Eu acho que a resposta é que depende. Seu exemplo de código (como você indica indiretamente) está simplesmente retornando a avaliação de uma condição e é melhor representado como você obviamente sabe, como uma única expressão.
Para determinar se a adição de uma condição else esclarece ou oculta o código, você precisa determinar o que o IF / ELSE representa. É uma expressão (como no seu exemplo)? Nesse caso, essa expressão deve ser extraída e usada. É uma condição de guarda? Nesse caso, o ELSE é desnecessário e enganoso, pois faz com que os dois ramos pareçam equivalentes. É um exemplo de polimorfismo processual? Extraia-o. Está definindo pré-condições? Então pode ser essencial.
Antes de decidir incluir ou eliminar o ELSE, decida o que ele representa, que informará se ele deve estar presente ou não.
fonte
A resposta é um pouco subjetiva. Um
else
bloco pode ajudar na legibilidade, estabelecendo que um bloco de código seja executado exclusivamente em outro bloco de código, sem a necessidade de ler os dois blocos. Isso pode ser útil se, digamos, ambos os blocos forem relativamente longos. Existem casos, embora onde terif
s semelse
s possa ser mais legível. Compare o seguinte código com um número deif/else
blocos:com:
O segundo bloco de código altera as
if
instruções aninhadas em cláusulas de guarda. Isso reduz a indentação e torna algumas das condições de retorno mais claras ("sea != b
, então retornaremos0
!")Foi indicado nos comentários que pode haver alguns buracos no meu exemplo, por isso vou detalhar um pouco mais.
Poderíamos ter escrito o primeiro bloco de código aqui para torná-lo mais legível:
Esse código é equivalente aos dois blocos acima, mas apresenta alguns desafios. A
else
cláusula aqui conecta essas instruções if juntas. Na versão da cláusula de guarda acima, as cláusulas de guarda são independentes uma da outra. Adicionar um novo significa simplesmente escrever um novoif
entre o últimoif
e o restante da lógica. Aqui, isso também pode ser feito, com apenas um pouco mais de carga cognitiva. A diferença, porém, é quando alguma lógica adicional precisa ocorrer antes dessa nova cláusula de guarda. Quando as declarações eram independentes, poderíamos fazer:Com a
if/else
corrente conectada , isso não é tão fácil de fazer. De fato, o caminho mais fácil a seguir seria quebrar a corrente para parecer mais com a versão da cláusula de guarda.Mas realmente, isso faz sentido. Usar
if/else
fracamente implica um relacionamento entre as partes. Poderíamos supor que issoa != b
esteja de alguma forma relacionadoc > d
naif/else
versão encadeada. Essa suposição seria enganosa, como podemos ver na versão da cláusula de guarda que elas não são, de fato, relacionadas. Isso significa que podemos fazer mais com cláusulas independentes, como reorganizá-las (talvez para otimizar o desempenho da consulta ou melhorar o fluxo lógico). Também podemos ignorá-los cognitivamente quando passamos por eles. Em umaif/else
cadeia, tenho menos certeza de que a relação de equivalência entrea
eb
não aparecerá mais tarde.O relacionamento implícito
else
também é aparente no código de exemplo profundamente aninhado, mas de uma maneira diferente. Aselse
instruções são separadas da condicional à qual estão anexadas e estão relacionadas em ordem inversa às condicionais (a primeiraelse
é anexada à últimaif
, a últimaelse
é anexada à primeiraif
). Isso cria uma dissonância cognitiva, na qual precisamos recuar no código, tentando alinhar a indentação em nossos cérebros. É como tentar combinar um par de parênteses. Fica ainda pior se o recuo estiver errado. Aparelhos opcionais também não ajudam.Esta é uma boa concessão, mas realmente não invalida nenhuma resposta. Eu poderia dizer que no seu exemplo de código:
uma interpretação válida para escrever código como esse poderia ser que "só devemos sair com um guarda-chuva se o céu não estiver azul". Aqui há uma restrição de que o céu deve estar azul para que o código a seguir seja válido para execução. Eu conheci a definição de sua concessão.
E se eu disser que, se a cor do céu é preta, é uma noite clara, então não é necessário um guarda-chuva. (Existem maneiras mais simples de escrever isso, mas existem maneiras mais simples de escrever o exemplo inteiro.)
Como no exemplo maior acima, eu posso simplesmente adicionar a nova cláusula sem muito pensamento necessário. Em um caso
if/else
, não posso ter tanta certeza de que não vou estragar nada, principalmente se a lógica for mais complicada.Também apontarei que seu exemplo simples também não é tão simples. Um teste para um valor não binário não é o mesmo que o inverso desse teste com resultados invertidos.
fonte
else
bloco é essencial para facilitar a leitura.Você simplificou demais o seu exemplo. Qualquer uma das alternativas pode ser mais legível, dependendo da situação maior: especificamente, depende se um dos dois ramos da condição é anormal . Deixe-me mostrar: em um caso em que um dos ramos é igualmente provável, ...
... é mais legível do que ...
... porque o primeiro exibe estrutura paralela e o segundo não. Mas, quando a maior parte da função é dedicada a uma tarefa e você só precisa verificar algumas pré-condições primeiro, ...
}
... é mais legível do que ...
... porque deliberadamente não configurar uma estrutura paralela fornece uma pista para um futuro leitor de que obter informações "realmente do tipo dois" aqui é anormal . (Na vida real,
ProcessInputDefinitelyOfTypeOne
não seria uma função separada, portanto a diferença seria ainda mais acentuada.)fonte
if
-> Faça o excepcional e deixe« como uma estratégia de retorno antecipado . Pense no código, no qual o padrão inclui mais verificações, seria mais rápido lidar com o caso excepcional primeiro.when using an if block to apply a constraint that must be logically satisfied for all following code, such as a null-check (or any other guards)
. Logicamente, qualquer trabalhoProcessInputOfTypeOne
depende do fato de que!InputIsReallyTypeTwo(input)
, portanto, precisamos capturá-lo fora do nosso processamento normal. NormalmenteProcessInputOfTypeTwo(input);
seria realmente othrow ICan'tProccessThatException
que tornaria a semelhança mais óbvia.u+
" geralmente introduz um token "unicode-range", mas se o próximo caractere não for um dígito hexadecimal, então é necessário fazer backup e processar o 'u' como um identificador. Portanto, o loop principal do scanner despacha para "escanear um token de alcance unicode" de maneira um tanto imprecisa e começa com "... se estivermos nesse caso especial incomum, faça backup e chame a sub-rotina scan-an-identifier".ProcessInputOfTypeOne
usa uma verificação imprecisa, mas rápida, que às vezes pega o tipo dois.Sim, há um aumento na complexidade porque a cláusula else é redundante . Portanto, é melhor deixar de fora para manter o código enxuto e mesquinho. Está no mesmo sentido que omitir
this
qualificadores redundantes (Java, C ++, C #) e omitir parênteses nasreturn
instruções, e assim por diante.Um bom programador pensa "o que posso adicionar?" enquanto um grande programador pensa "o que posso remover?".
fonte
else
bloco, se é explicado em uma única linha (o que não é frequente), geralmente é com esse tipo de raciocínio, então +1 por apresentar o argumento "padrão" que vejo, mas não o vejo ache que é um raciocínio forte o suficiente.A good programmer things "what can I add?" while a great programmer things "what can I remove?".
parece ser um ditado comum que muitas pessoas estendem demais sem consideração cuidadosa, e eu pessoalmente acho que esse é um desses casos.Eu notei, mudando de idéia sobre este caso.
Quando essa pergunta foi feita há um ano, eu teria respondido algo como:
»Deve haver apenas um
entry
e umexit
em um método« E com isso em mente, eu sugeriria refatorar o código para:Do ponto de vista da comunicação, é claro o que deve ser feito.
If
algo é o caso, manipule o resultado de uma maneira ,else
manipule-o de outra maneira .Mas isso envolve um desnecessário
else
. Oelse
expressa o caso padrão . Então, em um segundo passo, eu poderia refatorá-lo paraEntão surge a pergunta: por que usar uma variável temporária? O próximo passo da refatoração leva a:
Portanto, isso está claro no que faz. Eu daria um passo além:
Ou para os fracos de coração :
Você pode ler assim: »CanLeaveWithoutUmbrella retorna um bool . Se nada de especial acontecer, ele retornará false «Esta é a primeira leitura, de cima para baixo.
E então você "analisa" a condição »Se a condição for atendida, ela retornará verdadeira « Você poderia pular completamente a condicional e ter entendido o que essa função faz na caixa- padrão . Seus olhos e sua mente só precisam folhear algumas letras no lado esquerdo, sem ler a parte à direita.
Sim isso está certo. Mas essa informação é redundante . Você tem um caso padrão e uma "exceção" que é coberta pela
if
declaração.Ao lidar com estruturas mais complexas, eu escolheria outra estratégia, omitindo o
if
irmão ou o feioswitch
.fonte
So this is clear in what it does...
e expandir nele? (Os casos anteriores também são de relevância questionável). Digo isso porque é o que a pergunta está fazendo, examinamos. Você declarou sua posição, omitindo o bloco else, e é na verdade a posição que precisa de mais explicações (e a que me levou a fazer essa pergunta). Da pergunta:I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
Para resumir uma longa história, nesse caso, livrar-se da
else
palavra-chave é uma coisa boa. É totalmente redundante aqui e, dependendo de como você formata seu código, ele adiciona uma a três linhas completamente inúteis ao seu código. Considere o que está noif
bloco:Portanto, se o
if
bloco fosse inserido, todo o restante da função não é, por definição, executado. Mas se não for inserido, como não há maisif
instruções, todo o restante da função é executado. Seria totalmente diferente se umareturn
declaração não estivesse noif
bloco; nesse caso, a presença ou ausência de umelse
bloco teria um impacto, mas aqui, não teria um impacto.Na sua pergunta, depois de inverter sua
if
condição e omitirelse
, você declarou o seguinte:Eles precisam ler o código um pouco mais de perto então. Usamos linguagens de alto nível e uma organização clara do código para atenuar esses problemas, mas adicionar um
else
bloco completamente redundante adiciona "ruído" e "confusão" ao código, e também não devemos inverter ou inverter novamente nossasif
condições. Se eles não conseguem perceber a diferença, não sabem o que estão fazendo. Se eles podem dizer a diferença, eles sempre podem inverter asif
condições originais .Isso é essencialmente o que está acontecendo aqui. Você está retornando do
if
bloco, para que aif
avaliação da condiçãofalse
seja uma restrição que deve ser logicamente satisfeita para todo o código a seguir.Não, isso constituirá uma diminuição na complexidade do seu código e pode até constituir uma diminuição no código compilado, dependendo se certas otimizações super triviais ocorrerão ou não.
fonte
No exemplo específico que você deu, ele faz.
Eu acho que não poderia ficar mais simples do que isso:
fonte
if
declaração. De fato, desencorajo explicitamente uma simplificação adicional usando o código exato que você usou. Além disso, a complexidade ciclomática não é aumentada peloelse
bloco.Eu sempre tento escrever meu código de forma que a intenção seja a mais clara possível. Seu exemplo está faltando algum contexto, então vou modificá-lo um pouco:
Nossa intenção parece ser que possamos sair sem um guarda-chuva quando o céu estiver azul. No entanto, essa simples intenção é perdida em um mar de códigos. Existem várias maneiras de facilitar a compreensão.
Em primeiro lugar, há sua pergunta sobre o
else
ramo. Eu não me importo de qualquer maneira, mas tendem a deixar de fora oelse
. Entendo o argumento de ser explícito, mas minha opinião é que asif
instruções devem agrupar ramos 'opcionais', comoreturn true
esse. Eu não chamaria oreturn false
ramo de 'opcional', pois está agindo como nosso padrão. De qualquer forma, vejo isso como um problema muito menor e muito menos importante do que os seguintes:Uma questão muito mais importante é que suas filiais estão fazendo muito! Ramos, como tudo, devem ser "o mais simples possível, mas não mais". Nesse caso, você usou uma
if
instrução, que se ramifica entre blocos de código (coleções de instruções) quando tudo o que você realmente precisa se ramificar são expressões (valores). Isso fica claro, pois a) seus blocos de código contêm apenas instruções únicas eb) são a mesma instrução (return
). Podemos usar o operador ternário?:
para restringir o ramo apenas à parte diferente:Agora chegamos à redundância óbvia de que nosso resultado é idêntico ao
this.color == Color.Blue
. Sei que sua pergunta nos pede para ignorar isso, mas acho realmente importante ressaltar que essa é uma maneira processual de primeira ordem: você está quebrando os valores de entrada e construindo alguns valores de saída. Tudo bem, mas se possível, é muito mais poderoso pensar em termos de relacionamentos ou transformações entre a entrada e a saída. Nesse caso, obviamente, o relacionamento é o mesmo, mas muitas vezes vejo código processual complicado como esse, que obscurece um relacionamento simples. Vamos em frente e faça esta simplificação:A próxima coisa que gostaria de salientar é que sua API contém um duplo negativo: é possível para
CanLeaveWithoutUmbrella
que sejafalse
. Obviamente, isso simplifica oNeedUmbrella
sertrue
, então vamos fazer o seguinte:Agora podemos começar a pensar no seu estilo de codificação. Parece que você está usando uma linguagem orientada a objetos, mas usando
if
instruções para ramificar entre blocos de código, você acabou escrevendo código processual .No código orientado a objetos, todos os blocos de código são métodos e todas as ramificações são feitas por meio de despacho dinâmico , por exemplo. usando classes ou protótipos. É discutível se isso simplifica as coisas, mas deve tornar seu código mais consistente com o restante do idioma:
Observe que o uso de operadores ternários para ramificar entre expressões é comum em funções programação .
fonte
I ask that you ignore the many other ways the function can be written, and changes that can be made to it. (I know most people are itching to write return sky.Color == Color.Blue.)
. Na verdade, você usa a linha exata de código que mencionei. Além disso, embora essa parte da pergunta esteja fora do tópico, eu diria que você está indo longe demais em sua inferência. Você mudou radicalmente qual é o código.return false;
vs,else { return false; }
embora não se preocupe com polaridade de ramificação, despacho dinâmico, ternários, etc. Se você estiver escrevendo código de procedimento em uma linguagem OO, é necessária uma mudança radical;)if/else
declaração e o que é polaridade de ramificação.