No mundo do desenvolvimento de software, a qualidade do código é um fator determinante para a manutenção, evolução e sucesso de um projeto. Uma das práticas fundamentais para alcançar um código-limpo é a utilização de funções pequenas e focadas. Conforme discutido por Robert C. Martin em seu livro “Código-Limpo”, funções pequenas são essenciais para criar um código mais legível, testável e sustentável. Neste artigo, vamos explorar a importância das funções pequenas, as vantagens de adotá-las e como implementá-las de forma eficaz.
Por que é importante um código-limpo?
Código-limpo é aquele que é fácil de ler, entender e modificar. Manter um código-limpo traz várias vantagens, incluindo:
- Legibilidade: Um código fácil de ler é mais fácil de entender, o que é crucial para a colaboração entre desenvolvedores.
- Manutenção: Código claro e bem-estruturado facilita a manutenção e a refatoração, reduzindo o tempo e esforço necessários para essas tarefas.
- Colaboração: Facilita a colaboração entre desenvolvedores, permitindo que todos possam rapidamente entender a lógica e o fluxo do código.
- Redução de Erros: Código bem-organizado e legível é menos propenso a erros, pois é mais fácil identificar e corrigir problemas.
Os principais pilares
Os principais pilares do código-limpo são práticas e princípios que guiam os desenvolvedores na criação de código de alta qualidade. Cada pilar aborda um aspecto específico do desenvolvimento de software, contribuindo para a criação de um sistema que é fácil de entender, manter e evoluir. Aqui estão os principais pilares do código-limpo:
- Nomes significativos
- Funções pequenas e focadas
- Comentários eficientes
- Formatação consistente
- Tratamento adequado de erros
- Estrutura coesa e baixo acoplamento
- Testes automatizados
- Refatoração contínua
- Código simples e direto
- Princípios SOLID
Neste artigo, focaremos no segundo pilar: Funções Pequenas.
Segundo pilar: Funções Pequenas e Focadas
Funções pequenas e focadas são essenciais para a legibilidade e manutenibilidade do código. A ideia é que cada função deve realizar uma única tarefa ou ter uma única responsabilidade, tornando-a mais fácil de entender, testar e manter.
Vantagens de Funções Pequenas
- Legibilidade: Funções pequenas são mais fáceis de ler e entender. Quando uma função é curta, é mais provável que um desenvolvedor consiga entender o que ela faz rapidamente.
- Manutenção: Funções pequenas são mais fáceis de manter. Alterar uma pequena parte do código em uma função curta é mais seguro e menos propenso a introduzir erros do que modificar uma grande função que realiza muitas tarefas.
- Testabilidade: Funções pequenas são mais fáceis de testar. É mais simples escrever testes unitários para funções que têm uma única responsabilidade, o que melhora a cobertura de testes e a confiabilidade do software.
- Reutilização: Funções focadas podem ser reutilizadas em diferentes partes do código. Quando uma função realiza uma tarefa específica, ela pode ser chamada em diferentes contextos, evitando a duplicação de código.
Implementação de Funções Pequenas e Focadas
- Identificar Responsabilidades: O primeiro passo é identificar as diferentes responsabilidades dentro de uma função grande. Cada bloco de código que realiza uma tarefa distinta deve ser extraído para uma função separada.
- Nomear Funções de Forma Clara: As funções devem ter nomes que descrevam claramente o que fazem. Nomes bem escolhidos aumentam a legibilidade e tornam o código mais autoexplicativo.
- Manter o Tamanho Ideal: Embora não haja uma regra rígida sobre o tamanho exato de uma função, um bom guia é que a função deve caber na tela sem precisar rolar. Idealmente, uma função deve ter entre 5 e 15 linhas de código.
- Evitar Parâmetros Excessivos: Funções pequenas e focadas tendem a ter poucos parâmetros. Se uma função precisa de muitos parâmetros, isso pode ser um sinal de que ela está fazendo mais do que deveria e pode ser subdividida ainda mais.
- Refatoração Contínua: A criação de funções pequenas é um processo contínuo. À medida que o código evolui, novas oportunidades para extrair funções menores podem surgir. Refatorar regularmente para manter funções pequenas é uma prática recomendada.
04 exemplos em Delphi com Antes e Depois
- Processamento de Dados
Antes: Funções longas que processam dados de várias formas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
procedure TForm1.ProcessarDados; var Arquivo: TextFile; Linha: string; begin if FileExists('dados.txt') then begin AssignFile(Arquivo, 'dados.txt'); Reset(Arquivo); while not Eof(Arquivo) do begin Readln(Arquivo, Linha); // Processar a linha if Linha <> '' then begin // Lógica complexa aqui end; end; CloseFile(Arquivo); end; end; |
Depois: Funções menores e focadas em tarefas específicas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
procedure TForm1.ProcessarDados; begin if ArquivoExiste('dados.txt') then ProcessarArquivo('dados.txt'); end; function TForm1.ArquivoExiste(const NomeArquivo: string): Boolean; begin Result := FileExists(NomeArquivo); end; procedure TForm1.ProcessarArquivo(const NomeArquivo: string); var Arquivo: TextFile; Linha: string; begin AssignFile(Arquivo, NomeArquivo); Reset(Arquivo); try while not Eof(Arquivo) do begin Readln(Arquivo, Linha); ProcessarLinha(Linha); end; finally CloseFile(Arquivo); end; end; procedure TForm1.ProcessarLinha(const Linha: string); begin if Linha <> '' then ExecutarLogicaComplexa(Linha); end; procedure TForm1.ExecutarLogicaComplexa(const Linha: string); begin // Lógica complexa aqui end; |
Perceba nesse primeiro exemplo, que criamos outros métodos que possuem suas próprias responsabilidades. A função principal “ProcessarDados” ficou apenas com 02 linhas, diferentemente da versão anterior, onde fazia mais do que apenas processar os arquivos.
- Cálculo de Total
Antes: Funções que calculam total de várias formas em um único método.
1 2 3 4 5 6 7 8 9 10 11 |
procedure TForm1.CalcularTotal; var i, total: Integer; begin total := 0; for i := 0 to ContagemItens - 1 do total := total + Itens[i].Valor; ShowMessage('Total: ' + IntToStr(total)); end; |
Depois: Funções separadas para cálculos específicos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
procedure TForm1.CalcularTotal; var total: Integer; begin total := CalcularTotalDosItens; ExibirTotal(total); end; function TForm1.CalcularTotalDosItens: Integer; var i: Integer; begin Result := 0; for i := 0 to ContagemItens - 1 do Result := Result + Itens[i].Valor; end; procedure TForm1.ExibirTotal(total: Integer); begin ShowMessage('Total: ' + IntToStr(total)); end; |
Tenho certeza que está pensando: Mas a função já era pequena antes. Pois bem, reduzimos ainda mais e separamos as responsabilidades.
- Conexão com Banco de Dados
Antes: Função única que lida com conexão, consulta e processamento de resultados.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
procedure TForm1.ConectarEBuscarDados; var Conn: TADOConnection; Query: TADOQuery; ConnectionString: string; begin // Configurar a conexão Conn := TADOConnection.Create(nil); try ConnectionString := 'Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=MeuBanco;Data Source=MeuServidor'; Conn.ConnectionString := ConnectionString; Conn.LoginPrompt := False; Conn.Open; // Configurar e executar a consulta Query := TADOQuery.Create(nil); try Query.Connection := Conn; Query.SQL.Text := 'SELECT * FROM MinhaTabela'; Query.Open; // Processar os resultados while not Query.Eof do begin // Processar cada linha de resultados ShowMessage(Query.FieldByName('MeuCampo').AsString); Query.Next; end; finally Query.Free; end; finally Conn.Free; end; end; |
Depois: Funções separadas para cada responsabilidade.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
procedure TForm1.ConectarEBuscarDados; var Conn: TADOConnection; begin Conn := ConectarAoBancoDeDados; try BuscarEDisplayDados(Conn); finally Conn.Free; end; end; function TForm1.ConectarAoBancoDeDados: TADOConnection; var ConnectionString: string; begin Result := TADOConnection.Create(nil); ConnectionString := 'Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=MeuBanco;Data Source=MeuServidor'; Result.ConnectionString := ConnectionString; Result.LoginPrompt := False; Result.Open; end; procedure TForm1.BuscarEDisplayDados(Conn: TADOConnection); var Query: TADOQuery; begin Query := TADOQuery.Create(nil); try Query.Connection := Conn; Query.SQL.Text := 'SELECT * FROM MinhaTabela'; Query.Open; ProcessarResultados(Query); finally Query.Free; end; end; procedure TForm1.ProcessarResultados(Query: TADOQuery); begin while not Query.Eof do begin // Processar cada linha de resultados ShowMessage(Query.FieldByName('MeuCampo').AsString); Query.Next; end; end; |
- Envio de Email
Antes: Função única que lida com a construção e envio de email.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
procedure TForm1.EnviarEmail(destinatario, assunto, mensagem: string); var SMTP: TIdSMTP; Msg: TIdMessage; begin SMTP := TIdSMTP.Create(nil); Msg := TIdMessage.Create(nil); try // Configurar o servidor SMTP SMTP.Host := 'smtp.exemplo.com'; SMTP.Port := 25; SMTP.Username := 'usuario'; SMTP.Password := 'senha'; // Configurar a mensagem Msg.From.Address := 'meuemail@exemplo.com'; Msg.Recipients.EmailAddresses := destinatario; Msg.Subject := assunto; Msg.Body.Text := mensagem; // Enviar o email SMTP.Connect; try SMTP.Send(Msg); finally SMTP.Disconnect; end; finally SMTP.Free; Msg.Free; end; end; |
Depois: Funções separadas para construir e enviar o email.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
procedure TForm1.EnviarEmail(destinatario, assunto, mensagem: string); var email: TIdMessage; begin email := ConstruirEmail(destinatario, assunto, mensagem); try EnviarEmailSMTP(email); finally email.Free; end; end; function TForm1.ConstruirEmail(destinatario, assunto, mensagem: string): TIdMessage; var Msg: TIdMessage; begin Msg := TIdMessage.Create(nil); try Msg.From.Address := 'meuemail@exemplo.com'; Msg.Recipients.EmailAddresses := destinatario; Msg.Subject := assunto; Msg.Body.Text := mensagem; Result := Msg; except Msg.Free; raise; end; end; procedure TForm1.EnviarEmailSMTP(email: TIdMessage); var SMTP: TIdSMTP; begin SMTP := TIdSMTP.Create(nil); try // Configurar o servidor SMTP SMTP.Host := 'smtp.exemplo.com'; SMTP.Port := 25; SMTP.Username := 'usuario'; SMTP.Password := 'senha'; // Enviar o email SMTP.Connect; try SMTP.Send(email); finally SMTP.Disconnect; end; finally SMTP.Free; end; end; |
Conclusão
Funções pequenas e focadas são essenciais para alcançar um código-limpo e de alta qualidade. Elas melhoram significativamente a legibilidade, facilitam a manutenção e aumentam a testabilidade do código. Ao dividir responsabilidades em funções menores, os desenvolvedores criam um código mais modular e fácil de entender, o que, por sua vez, promove uma maior colaboração e eficiência dentro da equipe. Implementar funções pequenas não só reduz a complexidade, mas também permite uma melhor reutilização de código e uma refatoração contínua mais segura. Ao adotar essa prática, os desenvolvedores garantem que seu software não apenas funcione bem hoje, mas também possa evoluir e se adaptar às necessidades futuras com mais facilidade e menos risco de introduzir novos problemas. No próximo artigo, exploraremos o terceiro pilar do código-limpo: Comentários Eficientes.
Comunidade no Telegram
🚀Comente no campo abaixo 👇👇👇 o que achou e qual sua dúvida.
Te vejo na próxima
Adriano Santos
Demais Artigos:
Parte 1: Nomes Significativos
Parte 2: Funções Pequenas
Parte 3: Comentários Eficientes
Parrte 4: Formatação Consistente
Parte 5: Tratamento de Erros
Parte 6: Estrutura de Classes
Parte 7: Testes Automatizados
Parte 8: Refatoração Contínua
Parte 9: Código Simples e Direto
Parte 10: SOLID
Parabéns Adriano.
Obrigado