Voltar a pagina inicial
D3_Artigos D3_Projetos D3_Membros D3_Feedback D3_Forum

 
Artigo
 
Autor: NeoRevolutions
Categoria: Programação / Design de Software
Criado em: 09-03-2008 21:56:31
Atualizado em: 10-03-2008 21:56:31
Media: 0
Visitas: 299

Clique aqui para exibir o modo de impressão.

Organizando blocos if-else


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:


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.


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:


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:


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:


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:

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:


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.


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");


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.

Referencias


Code Complete - Steve McConnell - Microsoft Press




Todos os artigos da Developers3Soft podem ser copiados e redistribuidos livremente.
Apenas considere que os creditos devem ser mantidos e que não se cobre nenhuma taxa pelos textos, em parte ou em sua totalidade. Podem ser também inseridos em sua página se assim desejar ou criar um conjunto de textos selecionados em um unico arquivo.
Qualquer alteração deve ser indicada, assim como a data de modificação. A única restrição de alteração é manter os creditos originais e a origem do texto.
Faça bom proveito.

De sua nota para este artigo.
 

Developers3