C# -  Otimizando o código que usa o bloco if-else


 Hoje veremos 3 abordagens para otimizar o código quando temos uma lógica complexa que usa o bloco if-else na linguagem C#.

Às vezes, nosso código requer uma lógica condicional complexa, e isso pode fazer com que o código tenha as instruções que tornem difícil ler e depurar.

Considerando este cenário veremos 3 abordagens que podemos usar para simplificar instruções if-else complexas tornando-as mais fáceis de ler, entender e manter.

Quando usamos uma instrução if, geralmente avaliamos um valor verdadeiro/falso e executamos uma ação de em conformidade. Desta forma, nosso programa não roda em linha reta, mas pode mudar seu comportamento quando necessário.

Quando expandimos as instruções if com operadores lógicos, avaliamos uma lógica complexa que determina exatamente quando nosso código if deve ser executado. Mas às vezes isso torna o código mais complexo se houver condições que são difíceis de entender e ainda mais difíceis de consertar.

Para otimizar o código nestes cenários vamos considerar 3 abordagens:

  1. Quando dividimos parte da condição de uma instrução if em uma instrução if aninhada, ambas as instruções if obtêm uma condição que é mais fácil de ler (e faz mais sentido lógico).
     
  2. Com variáveis ​​provisórias, podemos substituir expressões booleanas longas e complexas por nomes descritivos. Isso torna mais claro do que se trata a instrução if.
     
  3. E evitamos o recuo excessivo na indentação quando substituímos várias instruções if aninhadas por uma única instrução if. Isso torna o código mais compacto, o que torna mais fácil visualizar o que a instrução if realmente faz.

Percebemos que as abordagens acima descritas adotam princípios simples mas que são bem efetivos. Assim vamos detalhar um pouco mais cada uma delas.

1- Simplificando instruções if complexas com ifs aninhados

Para transformar uma declaração if regular em uma declaração if aninhada, há um requisito:

  1. A declaração if original tem que usar o operador AND lógico do C# (&&).

Esse operador junta duas expressões verdadeiro/falso. Ele retorna verdadeiro quando o valor à sua esquerda e o valor à sua direita também forem verdadeiros. Quando uma ou ambas as expressões são falsas, o resultado combinado com && também é falso.

O operador && se comporta de maneira semelhante a como uma instrução if aninhada se comporta. Onde && requer duas expressões verdadeiras, o código de uma instrução if aninhada só é executado quando sua condição e a de sua instrução if superior forem ambas verdadeiras.

Veja o trecho de código a seguir:

if (condicaoA  && condicaoB )
{

   //código executado quando ambas as condições forem true
}

Podemos transformar essa instrução if em uma instrução if regular com uma instrução if aninhada.

Para fazer isso, dividimos a condição da instrução if original no local de &&. Isso muda o código para:

if (condicaoA)
{

   if(condicaoB)
   {

       //código executado quando ambas as condições forem true
   }

}

Agora, cada instrução if tem sua própria condição lógica. Mas o comportamento do código ainda corresponde ao código original. Ou seja, o código dentro dessa instrução if aninhada é executado nas mesmas situações em que a instrução if original foi executada.

Mas atenção !!!!

Quando uma instrução if usa o operador && várias vezes, podemos transformar essa instrução if única em várias instruções if aninhadas. Uma ou duas instruções if aninhadas ajudam a tornar o código mais fácil de ler. Mas muitas instruções if aninhadas perdem o ponto e, em vez disso, aumentam a complexidade do código.

2- Usando variáveis temporárias para simplificar instruções if complexas

A segunda maneira de simplificar as instruções if é com variáveis temporárias.

Essas variáveis têm um nome descritivo e armazenam parte de uma expressão mais longa, e, dessa forma, podemos substituir expressões longas e complicadas por nomes de variáveis mais claros, o que torna o código original mais fácil de ler e entender.

Vejamos o trecho de código a seguir:

...
if ((((anosExperiencia > 10) && !junior) || ((anosExperiencia > 5) && senior)) && (desempenho > 7.5))
{

       Console.WriteLine("Realizar promoção do funcionario para Master");
}

Esta instrução if tem várias expressões que avaliam se um funcionário ganhou uma promoção. Mas as expressões e o código aninhado entre parênteses não são uma instrução if intuitiva e fácil de ler.

Você saberia me dizer quando um funcionário será promovido ?

Podemos tornar o código mais claro e simples reescrevendo esta instrução if usando variáveis temporárias. Abaixo temos um exemplo:

  bool experienciaFuncionarioEmpresa = (anosExperiencia > 10) && !junior;
  bool experienciaFuncionarioSenior = (anosExperiencia > 5) && senior;
  bool desempenhoFuncionarioFuncao = (desempenho > 7.5);

  if ((experienciaFuncionarioEmpresa || experienciaFuncionarioSenior) && desempenhoFuncionarioFuncao)
  {
       Console.WriteLine("Promover para Master");
  }

O código acima ficou mais claro de ler e entender e o código da instrução if ainda é executado nas mesmas situações anteriores.

Usamos aqui  nomes de variáveis descritivos que esclarecem quais expressões avaliamos. E há menos parênteses, então é mais fácil ver como a condição if é avaliada.

3- Transformando instruções if aninhadas em uma única instrução if

Como já vimos, embora uma instrução if aninhada possa tornar o código mais fácil de ler (veja a dica acima), muitas instruções if aninhadas prejudicam a legibilidade do código.

Para combinar a lógica dos ifs aninhados em uma única instrução if, usamos o operador AND lógico do C# (&&). Este operador combina duas expressões booleanas em um único valor verdadeiro/falso.

Quando o valor à sua esquerda e o valor à sua direita são ambos verdadeiros, && também retorna verdadeiro. Mas se um ou ambos forem falsos, o resultado combinado com && também é falso.

Esse comportamento do operador é semelhante ao comportamento de uma instrução if aninhada. Afinal, o código dentro de uma instrução if aninhada só é executado quando as condições da instrução if superior e da instrução if aninhada forem verdadeiras. Quando uma ou ambas as condições são falsas, o código do ninho não é executado.

Para mostrar isso veja o exemplo de código a seguir:

 if (condicaoA)
 {
      if (condicaoB)
      {
            if (condicaoC)
            {

                 // codigo será executado
                 // quando todas as condições
                 // forem true

            }
       }
 }

O código desta última instrução if aninhada é executado apenas quando as condições das duas instruções if principais também forme avaliadas como verdadeiras.

Para simplificar este código em uma única instrução if, juntamos as três condições com o operador lógico AND (&&):

if (condicaoA  && condicaoB && condicaoC )
{
      // codigo será executado
      // quando todas as condições
      // forem true
}

Agora vejamos um exemplo mais real:

if (anosExperiencia > 10)
{
    if (!junior)
     {
         if (senior)
         {
             if (desempenho > 7.5)
             {
                  Console.WriteLine("Funcionário elegível.");
             }
          }
       }
}

Neste código estamos avaliamos quatro condições, cada uma com sua própria instrução if. Mas há muitos recuos de indentação e este código ocupa várias linhas, e,  isso ajuda a tornar este código difícil de entender.

Podemos simplificar o código transformando as instruções if aninhadas em uma única instrução. Para isso basta juntar as instruções usando o operador && :

if ((anosExperiencia > 10) && !junior && senior && (desempenho > 7.5))
{
       Console.WriteLine("Elegível para promoção.");
}

Sei o que você esta pensando !!!!

"O código desta instrução if agora nos leva a ter novamente uma instrução if complexa que tratamos na abordagem 2".

Sim, mas agora o código está mais compacto e não ocupamos vários níveis de indentação.   

Agora cabe destacar aqui um ponto muito importante.

Para transformar instruções if aninhadas em uma única instrução if, não deve haver uma instrução entre as diferentes instruções if. Quando houver, não podemos juntar as instruções if aninhadas em uma única instrução if.

Exemplo:

if (anosExperiencia > 10)
{
    if (!junior)
    {
       Console.WriteLine("Convidar para entrevista.");
         if (senior)
         {
                  Console.WriteLine("Funcionário elegível.");
         }
    }
}

Neste código temos duas instruções Console.WriteLine():

A primeira é executada com base em duas condições: anosExperiencia> 10 e !junior devem ser avaliados como true.

Mas essa instrução será executada independentemente do valor da variável desempenho.

A outra instrução Console.WriteLine() depende de três condições: anosExperienca>10, !junior e senior devem ser verdadeiros.

Desta forma como as duas instruções dependem de condições ligeiramente diferentes, não podemos juntá-las em uma única instrução if.

Por exemplo, o código abaixo está incorreto:

if (anosExperiencia > 10) && !junior && senior)
{
       Console.WriteLine("Convidar para entrevista.");
       Console.WriteLine("Funcionário elegível.");
}

O melhor que pode ser feito nestes casos é o seguinte:

if (anosExperiencia > 10)  && !junior)
{
    Console.WriteLine("Convidar para entrevista.");

    if (senior)
    {
            Console.WriteLine("Funcionário elegível.");
     }
}

Neste código, removemos uma instrução if aninhada, mas mantivemos a outra instrução if aninhada. Dessa forma, a primeira instrução Console.WriteLine() ainda é executada independentemente de qualquer valor que a variável senior tiver.

Apresentamos assim algumas opções que você pode considerar e aplicar para tornar o código que usa instruções if complexas mais simples.

"(Disse Jesus) Eu sou a videira verdadeira, e meu Pai é o agricultor. Todo ramo que, estando em mim, não dá fruto, ele corta; e todo que dá fruto ele poda, para que dê mais fruto ainda. "
Joao 15:1

Referências:


José Carlos Macoratti