Organizando condições (if e else)

Ver o tópico anterior Ver o tópico seguinte Ir em baixo

Qualidade Organizando condições (if e else)

Mensagem por M.A.S. em Sab 28 Mar 2009, 22:38

Artigo escrito por Neorevolutions, originário do site:
http://developers3.webcindario.com/articles/view.php?id=29&title=Organizando+blocos+if-else&PHPSESSID=1cc292ca2319952b4338ed094b881e2b

Introdução

Todo programador utiliza blocos de if-else, isso é certeza. Mas nem todo programador sabe utiliza-lo de maneira legível ou organizada, sendo ele experiente ou iniciante.

Dependendo de como se escreve um programa ou script, pode haver a necessidade de se utilizar grandes quantidades de blocos condicionais. Se há somente um ou dois blocos dentro de outro, não há muito motivo para se alarmar, mas quando a cadeia de blocos começa a aumentar, a legibilidade do código começa a diminuir drasticamente.


Blocos dentro de blocos

Toda confusão com blocos if-else começa quando se faz necessário que um determinado código somente seja executado após passar por uma série de testes. E em algumas ocasiões, a cada teste que falha, se necessita mostrar o erro ao cliente. Para se ter uma idéia do problema, veja o código abaixo:

Código:
if ( arquivo_existe )
{
    if ( arquivo_aberto )
    {
        if ( ler_linha_do_arquivo )
        {
            if ( linha_valida )
            {
                print( linha );
            }
            else
            {
                print("Linha inválida");
            }
        }
        else
        {
            print("Não foi possível ler a linha");
        }
    }
    else
    {
        print("Arquivo não aberto");
    }
}
else
{
    print("Arquivo não existe");
}

Esse código pode ser considerado horrível e o programador que escreve desta forma, deveria tentar corrigir o problema. Note que não existem tantos blocos if-else, mas mesmo assim dificulta um pouco seu entendimento.

Uma maneira melhor de se conseguir o mesmo objetivo do exemplo anterior, mas escrevendo de uma forma mais clara é não utilizando o else ou evitando de se colocar um bloco dentro de outro. Veja como ficaria o mesmo código escrito de outra forma.
Código:

if ( arquivo_existe == false )
{
    print("Arquivo não existe");
    return;
}

if ( arquivo_aberto == false )
{
    print("Arquivo não aberto");
    return;
}

if ( ler_linha_do_arquivo == false )
{
    print("Não foi possível ler a linha");
    return;
}

if ( linha_valida == false )
{
    print("Linha inválida");
    return;
}

print( linha );

Como pode ver, o bloco ficou mais legível, pois precisamos nos preocupar com um bloco de cada vez. No exemplo anterios teriamos que nos preocupar com o teste que está sendo lido no momento e também com os outros testes feitos anteriormente. Fica também mais dificil localizar onde está o código que irá ser executado se passar em todos os testes.

Além de tudo, o bloco else está muito longe de seu par, o que pode causar confusão em se saber qual else faz parte com um determinado if. Utilizando-se o segundo método pode eliminar ou pelo menos diminuir esse problema.

Esse primeiro exemplo não é dos piores, existem outros ainda mais confusos. Veja:

Código:
string erro;
status resultado;

resultado = abrir_arquivo( arquivo );
if ( resultado == status_erro )
{
    erro = "Arquivo nao aberto";
}
else
{
    resultado = ler_arquivo(arquivo, dados);
    if ( resultado == status_ok )
    {
        resultado = mostrar_dados( dados );
        if ( resultado == status_erro )
        {
            erro = "Dados invalidos";
        }
        else
        {
            resultado = salvar_dados( dados );
            if ( resultado == status_erro )
            {
                erro = "Dados não salvos";
            }
            else
            {
                limpar_dados( dados );
                erro = "Nenhum";
            }
        }
    }
    else
    {
        erro = "Erro de leitura";
    }
}


Esse código é dificil de se seguir por que os blocos de erro e estão misturados com os outros. Por este motivo, não é tão óbvio o caminho lógico a ser seguido. Um meio de se melhorar este código é utilizando o estilo do primeiro exemplo, mas isto também não seria a melhor solução. Veja como ficaria seguindo o estilo do primeiro exemplo:

Código:
string erro;
status resultado;

resultado = abrir_arquivo( arquivo );
if ( resultado == status_ok )
{
    resultado = ler_arquivo(arquivo, dados);
    if ( resultado == status_ok )
    {
        resultado = mostrar_dados( dados );
        if ( resultado == status_ok )
        {
            resultado = salvar_dados( dados );
            if ( resultado == status_ok )
            {
                limpar_dados( dados );
                erro = "Nenhum";
            }
            else
            {
                erro = "Dados não salvos";
            }
        }
        else
        {
            erro = "Dados invalidos";
        }
    }
    else
    {
        erro = "Erro de leitura";
    }
}
else
{
    erro = "Arquivo nao aberto";
}

Ficou mais legível, mas ainda não seria o ideal. Ainda há uma certa dificuldade em se perceber qual erro faz parte de qual teste. Para se aumentar a legibilidade, é prefirivel que este código fique isolado em uma função, retornando um status geral. Veja:

Código:
tipo_de_erro processar_arquivo( arquivo )
{
    status resultado;

    resultado = abrir_arquivo( arquivo );

    if ( resultado == status_erro )
        return Erro_Arquivo_Nao_Aberto;
       
    resultado = ler_arquivo(arquivo, dados);

    if ( resultado == status_erro )
        return Erro_Arquio_Nao_Lido;

    resultado = mostrar_dados( dados );

    if ( resultado == status_erro )
        return Erro_Dados_Invalidos;

    resultado = salvar_dados( dados );

    if ( resultado == status_erro )
        return Erro_Dados_Nao_Salvos;

    limpar_dados( dados );

    return Erro_Nenhum;
}

Perceba que ficou mais facil de se seguir a lógica pretendida e ainda melhorando a legibilidade desse código. Outro modo de se fazer a mesma coisa é o seguinte:
Código:

tipo_de_erro processar_arquivo( arquivo )
{
    if ( abrir_arquivo( arquivo ) == status_erro )
        return Erro_Arquivo_Nao_Aberto;
       
    if ( ler_arquivo(arquivo, dados) == status_erro )
        return Erro_Arquio_Nao_Lido;

    if ( mostrar_dados( dados ) == status_erro )
        return Erro_Dados_Invalidos;

    if ( salvar_dados( dados ) == status_erro )
        return Erro_Dados_Nao_Salvos;

    limpar_dados( dados );

    return Erro_Nenhum;
}

Este último código ficou ainda mais fácil de ser lido e entendido por qualquer um que o observe. Separando-se partes complexas em diferentes funções pode melhorar a legibilidade nos casos em que o tamanho das funções seja muito extenso.


Organizando conjuntos de testes

Outro problema que pode ocorrer num código é uma quantidade muito grande de testes relacionais ou lógicos. Isso tudo dentro de um único if.

Veja como seria um caso típico do problema:

Código:
if (
    indice < 0 ||
    indice > maximo ||
    indice == ultimo_indice ||
    dados_inseridos == false )
{
    print("Dados invalidos");
}

Esse é um exemplo simples, então ainda é legível. Mas se houvesse mais operadores relacionais e lógicos, o código se tornaria mais complexo e, portanto, mais dificil de se entender.

Veja como criar um código mais legível se um dia encontrar uma situação parecida.

Código:
bool indice_invalido = (indice < 0 || indice > maximo);
bool indice_repetido = (indice == ultimo_indice);
bool dados_inexistentes = (dados_inseridos == false );
if ( indice_invalido || indice_repetido || dados_inexistentes)
    print("Dados invalidos");
[code]

Este método clarifica o entendimento por ser mais fácil de se ler, pois fica parecido com uma lingua real ao invés de uma linguagem de programação.

O que seria mais fácil de se entender?

A: Se o indice for menor que zero ou o indice for maior que o máximo ou o indice é o mesmo que o anterior ou os dados não foram inseridos, então faça tal coisa.
B: Se o indice é inválido ou se o indice for repetido ou se os dados não foram inseridos, faça tal coisa.

Provavelmente respondeu B.

Hoje em dia, com a capacidade dos computadores, criar 3 ou 4 variáveis a mais praticamente não influencia na performance geral.


Conclusão

Todas estas informações são apenas dicas para todos os programadores que queiram melhorar seus códigos. Nada os impede de ignorá-las e seguir com o mesmo estilo. Mas será que há algo que o impeça de utilizá-los e criar códigos mais legíveis?

Assimo como qualquer dica, utilize apenas os que achar necessarios ou que for apropriado para cada situação.

M.A.S.

Número de Mensagens : 377
Data de inscrição : 03/01/2009
Reputação : 0
Insignia 1 x 0 Insignia 2 x 0 Insignia 3 x 0

Voltar ao Topo Ir em baixo

Ver o tópico anterior Ver o tópico seguinte Voltar ao Topo

- Tópicos similares

 
Permissão deste fórum:
Você não pode responder aos tópicos neste fórum