E aÃ, pessoal, tudo certo?
Há algumas semanas, compartilhei um dos artigos do blog em um dos grupos de Clean Code do LinkedIn e recebi alguns comentários na publicação. Um dos membros, chamado Max Kleiner, mencionou uma técnica bem bacana que pode colaborar com a escrita de código limpo. Trata-se do CODEsign. Leia o artigo e conheça as propostas dessa técnica!
Introdução
Como de costume, para facilitar a associação do termo, vale apresentar um exemplo logo de inÃcio.
Suponha que você desenvolveu, pela primeira vez, um aplicativo para exportar um conjunto de dados para uma planilha do Excel. Durante o desenvolvimento, você estudou e utilizou bibliotecas de I/O para gravar arquivos e algumas funções para trabalhar com exportação de dados. Perfeito.
Agora, faço uma pergunta: se fosse necessário desenvolver o mesmo projeto novamente, você faria diferente?
Eu aposto que sim!
A proposta do CODEsign
Levando em conta o conhecimento que você adquiriu desenvolvendo o projeto na primeira vez, é bem provável que, ao implementá-lo pela segunda vez, o código ficaria bem melhor. O motivo é óbvio. Com base na experiência anterior, aprimora-se a capacidade de elaborar algo mais profissional. O simples fato de criar um método com um nome mais expressivo já traz um aspecto mais profissional ao código.
Essa é a ideia do CODEsign: implementar a mesma funcionalidade duas vezes! Considera-se que, na segunda vez, há uma possibilidade significativa de escrever um código mais limpo. Você, desenvolvedor, já deve ter passado por essa experiência e sabe do que estou falando. 🙂
Coincidentemente, isso aconteceu comigo há alguns dias. Desenvolvi um miniprojeto para ler um arquivo texto e importar o conteúdo para uma base de dados (semelhante a um processo de ETL), porém, depois de finalizado, mesmo funcional, não fiquei satisfeito com a quantidade de linhas e com a estrutura do código. Decidi refazer o projeto, mas, como já tinha conhecimento das deficiências da primeira tentativa, considerei formas diferentes de escrever o código, e o resultado foi surpreendente! Criei uma classe de leitura herdando de estruturas nativas e declarei vários métodos que antes haviam sido repetidos, melhorando não só o código, mas também a arquitetura.
Na prática, apliquei o CODEsign! 🙂
Como o conceito do CODEsign é relativamente amplo, decidi reunir as partes mais voltadas para programação e aproveitei para acrescentar um pouco do meu conhecimento nessa ideia.
Para ter um efeito realmente satisfatório, o CODEsign consiste em ponderar cinco aspectos ao reescrever a funcionalidade, detalhados a seguir.
1) Simplicidade
Reflexão: “Existe uma forma mais simples de implementar a funcionalidade?”
Pode parecer óbvio, mas muitas vezes a simplicidade passa despercebida. O código abaixo faz a leitura de uma string e exibe os valores que estão separados por vÃrgula (nome, sobrenome e cidade):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
const VALORES = 'Andre;Celestino;Maringa;'; var lPosicaoSeparador, lPosicaoTexto: smallint; lNome, lSobrenome, lCidade: string; begin lPosicaoSeparador := PosEx(';', VALORES, 1); lPosicaoTexto := 1; lNome := Copy(VALORES, lPosicaoTexto, lPosicaoSeparador - lPosicaoTexto); lPosicaoTexto := lPosicaoSeparador + 1; lPosicaoSeparador := PosEx(';', VALORES, lPosicaoTexto); lSobrenome := Copy(VALORES, lPosicaoTexto, lPosicaoSeparador - lPosicaoTexto); lPosicaoTexto := lPosicaoSeparador + 1; lPosicaoSeparador := PosEx(';', VALORES, lPosicaoTexto); lCidade := Copy(VALORES, lPosicaoTexto, lPosicaoSeparador - lPosicaoTexto); ShowMessage(lNome); ShowMessage(lSobrenome); ShowMessage(lCidade); end; |
Hmm… o código ficou confuso, não?
Se pesquisarmos na documentação da linguagem, ou mesmo na internet, é possÃvel encontrar funções exclusivas (e, claro, mais simples) para essa finalidade, poupando toda a complexidade criada anteriormente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
const VALORES = 'Andre;Celestino;Delphi;'; var lListaValores: TStringList; i: smallint; begin lListaValores := TStringList.Create; ExtractStrings([';'], [' '], VALORES, lListaValores); for i := 0 to lListaValores.Count - 1 do begin ShowMessage(lListaValores[i]); end; FreeAndNil(lListaValores); end; |
2) Performance
Reflexão: “Existe uma forma mais rápida de executar a operação?”
Considere o código abaixo. Para cada produto, há um cálculo de desconto:
1 2 3 4 5 6 7 8 |
DataSetProdutos.First; while not DataSetProdutos.Eof do begin if DataSetProdutos.FieldByName('Valor').AsFloat > 0 then AplicarDesconto(DataSetProdutos.FieldByName('Codigo').AsInteger); DataSetProdutos.Next; end; |
A condição IF é realmente necessária? Imagine que, de 1000 produtos, apenas 200 deles possuem valor maior que zero. 800 iterações do loop de repetição seriam, na prática, em vão. Se aplicarmos um filtro antes do loop, terÃamos um ganho considerável de performance:
1 2 3 4 5 6 7 8 |
DataSetProdutos.Filter := 'Valor > 0'; DataSetProdutos.Filtered := True; DataSetProdutos.First; while not DataSetProdutos.Eof do begin AplicarDesconto(DataSetProdutos.FieldByName('Codigo').AsInteger); DataSetProdutos.Next; end; |
3) Integridade
Reflexão: “O código está suscetÃvel a erros?”
O código a seguir carrega o conteúdo de um arquivo dentro de um componente TMemo
:
1 |
Memo1.Lines.LoadFromFile('C:\Importacao\Entrada.txt'); |
Ops, mas espere aÅ se o arquivo não existir, ocorrerá uma exceção. É nosso dever garantir a integridade da execução do aplicativo.
1 2 3 4 5 6 7 |
if not FileExits('C:\Importacao\Entrada.txt') then begin ShowMessage('O arquivo de importação não está disponÃvel.'); Exit; end; Memo1.Lines.LoadFromFile('C:\Importacao\Entrada.txt'); |
4) Confiabilidade
Reflexão: “O código é capaz de tratar exceções e se recuperar de falhas?”
Dessa vez, o método abaixo faz o download de um arquivo em um diretório FTP para o computador:
1 2 |
IdFTP.Connect; IdFTP.Get('ftp/Arquivo.xml', 'C:\Aplicativo\Arquivo.xml'); |
Primeiramente, o que acontecerá se a conexão com a internet for interrompida durante o download? Em segundo lugar, é importante garantir que a conexão FTP seja fechada após a transferência do arquivo. Estes eventos podem ser solucionados com os tradicionais try/except e try/finally:
1 2 3 4 5 6 7 8 9 10 11 |
try try IdFTP.Connect; IdFTP.Get('ftp/Arquivo.xml', 'C:\Aplicativo\Arquivo.xml'); except ShowMessage('Falha no download. Verifique a conexão com a internet.'); ExcluirArquivosCorrompidos; end; finally IdFTP.Disconnect; end; |
5) Redundância
Reflexão: “É possÃvel evitar a duplicação de código?”
Algumas vezes, a redundância no código pode prejudicar a leitura do código. Veja o exemplo abaixo:
1 2 3 |
ConsultarVendasDoCliente(Query.FieldByName('Codigo').AsInteger); ConsultarPagamentosPendentes(Query.FieldByName('Codigo').AsInteger); EnviarEmailParaCliente(Query.FieldByName('Codigo').AsInteger); |
Notou a quantidade de vezes que o FieldByName é utilizado? Para evitar essa redundância, podemos declarar uma simples variável:
1 2 3 4 5 6 7 8 9 10 |
var lCodigoCliente: integer; begin ... lCodigoCliente := Query.FieldByName('Codigo').AsInteger; ConsultarVendasDoCliente(lCodigoCliente); ConsultarPagamentosPendentes(lCodigoCliente); EnviarEmailParaCliente(lCodigoCliente); end; |
Pessoal, os exemplos são bem básicos, mas suficientes para trazer um pouco da didática do CODEsign. Na prática, ao codificar uma funcionalidade pela segunda vez, além dos aspectos acima, o desenvolvedor pode descobrir novas abstrações, resultando na criação de novos componentes, bibliotecas ou classes, como foi o meu caso.
Creio que você já tenha se deparado com um projeto antigo e pensado: “Nossa, que código feio. Hoje eu faria completamente diferente…”, não é? Tecnicamente, não deixa de ser a prática do CODEsign! 🙂
Até a próxima, pessoal!
Muito bom, passei por isso na época de tcc na faculdade, a linguagem era PHP. Seis meses depois de entregue, voltei e modifiquei varias funções, muitas coisas, enfim.
Como agora a ferramenta é Delphi, aprendendo um pouco a cada dia com posts como esse. Valeu André, forte abraço!
Olá, Alex!
Obrigado pelo comentário! Acredito que a maioria (ou todos) programadores já passaram por uma situação parecida. Isso é gratificante, pois indica que adquirimos mais experiência desde que programamos pela primeira vez.
Abraço!
No teste 2, a performance vai para o espaco se o valor for igual a 0 “zero” rsrsrs
Olá, Manoel!
Meu Deus, tem razão! rsrs
Já ajustei o código. Obrigado pela observação!
Este dias eu participei do intensive Delphi, quando pode ouvir voce falar sobre o Firedac e varias formas de aproveitar melhor os componentes desta categoria do Delphi. Lembro que em algum momento voce disse que o seu blog estava desatualizado, mas em breve, estaria trabalhando nele novamente. Meu irmão, este seu blog esta parado há algum tempo, mas nunca me deixou na mão com as inúmeras dicas tão atuais no dias de hoje. Parabéns. Espero em breve que venha terminar o assunto Design Patterns GRASP. Estou estudando sobre isto, muito legal!
Olá, Manoel.
MuitÃssimo obrigado pelo feedback sobre o blog! São comentários como o seu que me motivam a continuar o trabalho por aqui.
Se tudo correr bem, pretendo retomar os artigos ainda esse semestre. Estou sentindo falta de escrever os artigos.
Grande abraço!