O Procedimento Exit


Todos os programadores Pascal conhecem o procedimento Exit() desde as primeiras versões dos compiladores. Mas será que todos sabem utilizá-lo corretamente?

Unsplash image Photo by Kev Seto on Unsplash

O procedimento Exit() é utilizado quando queremos sair de um escopo em execução. Esse escopo pode ser uma função, procedimento, método ou até mesmo o próprio programa.

Vamos dizer que um programa console chame um procedimento Execute:

procedure Execute;
begin
  Writeln('1. Passing on this line...');
  Exit;
  Writeln('2. It will not pass here');
end;

No exemplo acima, somente a informação do primeiro Writeln será mostrado no console.

Ao sair de um escopo, o programa retorna imediatamente para o escopo anterior (outra função/procedimento/método ou o próprio programa). A única exceção a essa regra é quando há blocos try-finally. Se Exit() for chamado dentro de um bloco try-finally o compilador irá executar o código dentro do finally-end antes de sair do escopo.

Aqui está outro exemplo:

procedure Execute;
begin
  try
    Writeln('1. Passing on this line...');
    Exit;
  finally
    Writeln('2. I am still here!');
  end;
  Writeln('3. It will not pass here');
end;

Os textos #1 e #2 serão mostrados no console. Mesmo que Exit() tenha sido chamado antes da impressão do texto #2, ainda sim o código é executado devido ao try-finally.

Outro exemplo de uso do Exit() é quando fazemos validações. Se uma validação ou checagem não retornar verdadeiro, utilizamos o Exit() para parar a execução do escopo atual.

Suponha que queremos somar dois números inteiros, mas só queremos números maiores que zero:

function Sum(A, B: Integer): string;
begin
  Result := 'Invalid result';
  if (A < 0) or (B < 0) then
    Exit;
  Result := Format('The result is %d', [A + B]);
end;

No exemplo acima, o retorno da função Sum é inicializado com um valor inválido e depois há uma validação para saber se os valores são menores que 0. Se o teste falhar, o programa irá retornar para o escopo anterior à chamada da função Sum com o resultado inválido. Mas se o teste não falhar, o resultado da função será a soma de A e B.

Há aqueles que são adeptos da programação estruturada e preferem não “quebrar” a execução do programa com uma “saída antecipada”, ou seja, não fazem uso do Exit() por acreditarem que o código ficaria mais simples.

Então vamos reescrever o exemplo anterior:

function Sum(A, B: Integer): string;
begin
  if (A > 0) and (B > 0) then
    Result := Format('The result is %d', [A + B]);
  else
    Result := 'Invalid result';
end;

Parece mais simples? Bem, nesse exemplo eu diria que sim. Porém para exemplos com mais condicionais, eu diria que não (vamos ver isso mais abaixo).

E se quiséssemos dizer ao usuário que seus dados não estão corretos?

function Sum(A, B: Integer): string;
begin
  Result := 'Invalid result';
  if (A > 0) then
  begin
    if (B > 0) then
      Result := Format('The result is %d', [A + B]);
    else
      Writeln('B should be greater than zero');
  end
  else
    Writeln('A should be greater than zero');
end;

Nesse exemplo não utilizamos Exit() e eu acho que o código está bem confuso. Os testes estão “separados” do retorno de aviso para o usuário (Writeln).

Kent Beck , Martin Fowler afirmaram categoricamente que “um ponto de saída não é realmente uma regra útil. A clareza é o princípio chave: se o método for mais claro com um ponto de saída, use um ponto de saída, caso contrário, não”

Então vamos reescrever o exemplo anterior com o uso de Exit():

function Sum(A, B: Integer): string;
begin
  Result := 'Invalid result';
  if (A < 0) then
  begin
    Writeln('A should be greater than zero');
    Exit;
  end;
  if (B < 0) then
  begin
    Writeln('B should be greater than zero');
    Exit;
  end;
  Result := Format('The result is %d', [A + B]);
end;

O código ficou um pouco maior, é verdade, porém os testes e avisos para o usuário ficaram mais simples, na minha opinião. Você não precisa acompanhar todos os if-else aninhados. A cada teste que falhar, o aviso está logo abaixo e o escopo será abortado com o uso do Exit(). Se todos os testes não falharem, a função irá retornar a soma de A e B.

No Delphi, a partir da versão 2009, o procedimento Exit() ganhou uma melhoria: Exit() pode ter um parâmetro especificando um resultado. O parâmetro deve ser do mesmo tipo que o resultado da função.

O FPC também tem a mesma definição, porém não sei quem implementou essa nova feature primeiro.

Então vamos reescrever o exemplo anterior:

function Sum(A, B: Integer): string;
begin
  if (A < 0) then
    Exit('A should be greater than zero');
  if (B < 0) then
    Exit('B should be greater than zero');
  Result := Format('The result is %d', [A + B]);
end;

Simples e limpo.

Exit() pode receber como parâmetro qualquer tipo de retorno, até mesmo instâncias de Interfaces. Utilizando esse parâmetro, é como se ganhássemos o mesmo comportamento da palavra reservada return, em Java. No entanto, Exit() em conjunto com Result nos dá ainda mais possibilidades de retorno para as funções.

Até logo.

Posts Relacionados

  • Memória Segura Utilizando Instâncias de Interfaces

  • Classes Mutáveis vs Objetos Imutáveis

  • Implementando Interfaces Utilizando Diferente Assinaturas de Métodos

  • Usando Paths ao invés de Diretivas de Compilação

  • Trabalhando com Exceções em Requisições HTTP

  • Tipo object Continua Vivo

  • Array de Objetos

  • Variáveis Locais Deveriam ter Nomes Curtos

  • Como Dividir e Organizar o Código em Formulários com Muitos Widgets

  • Pascal Deveria ser Modernizado?