Fala, pessoal, tudo certo?
Sabemos que refatoração, extração de métodos e sub-rotinas nos ajudam bastante na programação, principalmente quando o assunto é reutilização de código. Na declaração de novos métodos, algumas vezes é necessário criar alguns parâmetros para que o método execute adequadamente as operações internas. O artigo de hoje é justamente relacionado com estes parâmetros. Acompanhe!
Como parte das técnicas de melhoria da expressividade no código, os parâmetros de métodos também devem receber uma atenção especial. Durante o desenvolvimento de um projeto, estaremos em um constante trabalho de criação e consumo de vários métodos e gostarÃamos que a interpretação de cada um deles fosse simples. Desejamos que, ao acessar a declaração ou implementação de um método, seja fácil compreendê-lo. Por isso, a seguir, apresento 5 dicas sobre como aplicar as técnicas de Clean Code na escrita e utilização de parâmetros de métodos.
1) Nomenclatura
Essa dica é primordial, claro! Quando declaramos variáveis internas, empregamos prefixos e nomes que sejam condizentes com o que elas armazenarão, não é? Para os parâmetros de métodos, devemos seguir o mesmo critério, porém, com um detalhe: nomeá-los de uma forma particular para indicarmos que são parâmetros ao invés de variáveis. Uma sugestão é adicionar o prefixo “a”, conforme a convenção da própria Embarcadero.
1 2 |
procedure VerificarPedidosPendentes( aDataInicial, aDataFinal: TDateTime; aCodCliente: integer); |
Na implementação do método, saberemos que as variáveis que começam com o prefixo “a” são, na realidade, parâmetros. Uma das vantagens dessa dica é permitir uma melhor orientação durante a leitura do método, caso seja necessário realizar uma manutenção ou interpretar a regra de negócio.
2) Parâmetros constantes
Opa, tem algo faltando na dica anterior. Na implementação do método, não gostarÃamos que as datas inicial e final recebam valores diferentes, já que podem comprometer a funcionalidade ou mesmo o retorno. Devemos “proteger” os valores que vieram por parâmetro para que não sejam modificados. Para isso, podemos adicionar a palavra-chave const
na declaração dos parâmetros, indicando que só a leitura é permitida:
1 2 |
procedure VerificarPedidosPendentes( const aDataInicial, aDataFinal: TDateTime; const aCodCliente: integer); |
No exemplo acima, os valores de aDataInicial
, aDataFinal
e aCodCliente
não poderão ser alterados dentro do método.
3) Parâmetros default
Algumas vezes, pode ser necessário definir um valor padrão para um método, caso ele não seja informado. Dessa forma, o método irá funcionar como esperado, sem apresentar erros ou inconsistências devido à falta de algum dado. Bom, talvez você tenha pensado na seguinte solução:
1 2 3 4 5 6 7 |
procedure CalcularIPI(const aCodProduto: integer; aPorcentagemIPI: real); begin if aPorcentagemIPI = 0 then aPorcentagemIPI = 5; ... end; |
Porém, há uma forma mais “limpa” de definir este valor padrão. Basta indicá-lo na própria assinatura do método:
1 2 |
// declaração procedure CalcularIPI(const aCodProduto: integer; aPorcentagemIPI: real = 5); |
No método que o chama, por sua vez, nem precisaremos informar um valor para este parâmetro, já que, em sua ausência, o valor “5” será assumido:
1 2 3 4 5 6 7 |
var lCodProduto: integer; lValorIPI: real; begin lCodProduto := StrToInt(Edit1.Text); lValorIPI := CalcularIPI(lCodProduto); // apenas um parâmetro foi informado end; |
4) Quantidade de parâmetros
Quando o número de parâmetros começa a crescer (diga-se, mais de três parâmetros), o recomendado é criar um objeto que contenha todos esses valores e seja passado como um parâmetro único, dispensando o uso excessivo de vÃrgulas na chamada do método. Sendo assim, ao invés da chamada abaixo:
1 2 3 |
if EnviarEmailCliente(Edit1.Text, Memo1.Lines.Text, RadioGroup1.ItemIndex, ComboBox1.Text, ListBox1.Items.Text, OpenDialog1.FileName) then ShowMessage('E-mail enviado com sucesso!'); |
Eu recomendaria a forma a seguir:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
var objParametros: TParametros; begin objParametros := TParametros.Create; try objParametros.Assunto := Edit1.Text; objParametros.Mensagem := Memo1.Lines.Text; objParametros.TipoFormatacao := RadioGroup1.ItemIndex; objParametros.ContaEmail := ComboBox1.Text; objParametros.Destinatarios := ListBox1.Items.Text; objParametros.Anexo := OpenDialog1.FileName; if EnviarEmailCliente(objParametros) then ShowMessage('E-mail enviado com sucesso!'); finally FreeAndNil(objParametros); end; end; |
O número de linhas de código pode ser relativamente maior, mas, por outro lado, o código definitivamente se torna mais compreensÃvel.
5) Parâmetros de saÃda
A diferença entre procedimentos e funções é que essas retornam um valor para o método chamador, como a função abaixo, que retorna um valor do tipo integer:
1 2 3 4 5 6 7 8 9 10 11 |
// declaração function SomarQtdeComprasClientes: integer; { ... } // chamada do método var lQtdeCompras: integer; begin lQtdeCompras := SomarQtdeComprasCliente; ... end; |
Porém, em algumas ocasiões, é necessário que um método retorne mais de um valor. É aqui que os parâmetros de saÃda entram em ação como uma alternativa. No exemplo abaixo, duas variáveis são criadas no método chamador e passadas como parâmetros de saÃda para outro método (utilizando a palavra reservada out
do Delphi), no qual irá preenchê-las. Tecnicamente, então, podemos dizer que o método “retorna dois valores”.
1 2 3 4 5 6 7 8 9 10 11 12 |
// declaração procedure ConsultarDadosComprasCliente(out aQtdeCompras: integer; out aTotalCompras: real); { ... } // chamada do método var lQtdeCompras: integer; lTotalCompras: real; begin ConsultarDadosComprasCliente(lQtdeCompras, lTotalCompras); // após a chamada, as duas variáveis "voltam" preenchidas end; |
Agora, uma controvérsia. Muitos desenvolvedores provavelmente irão discordá-la, mas acho interessante compartilhar a minha visão. De modo geral, devo dizer que sou bastante cético quanto ao uso de parâmetros de saÃda. Um dos motivos é que, parâmetros, por si só, já são interpretados como entrada. Quando utilizamos um método, assumimos que os parâmetros que informaremos serão subsÃdios para que ele funcione, e, convenhamos, é a maneira lógica de se pensar. Na minha opinião, ao invés de criar um método que retorne dois valores, é melhor criar dois métodos que retornem apenas um valor.
Além disso, parâmetros de saÃda também podem confundir o desenvolvedor em alguns casos. Considere a chamada abaixo, baseada no livro de Robert C. Martin:
1 |
AdicionarTexto(aTexto); |
Como você a interpreta? A variável aTexto
é uma entrada (será adicionada a um texto já existente), ou uma saÃda (um texto será adicionado à variável aTexto
)? A solução, claro, é verificar a declaração do método. Se estiver em outra classe, será necessário acessá-la. Embora imperceptÃvel, esse esforço consome um tempinho. A minha sugestão é evitar parâmetros de saÃda sempre que possÃvel.
E você? Tem mais alguma recomendação? Deixe um comentário!
Abraço!
Excelente Artigo, muito bom mesmo, Parabéns! Continue escrevendo que está ajudando muitos desenvolvedores como eu. Obrigado!
Opa! Muito obrigado!
Pode deixar. Continuarei escrevendo mais artigos!
Abraço!
E aà André, tudo bem?
Muito bom o seu artigo, como de costume. Parabéns!
Entendi bem a solução para o excesso de parâmetros em métodos, o que eu estou em dúvida é quanto ao excessos de parâmetros em construtores. Pois passo como parâmetros no construtor os atributos da classe que dependendo da quantidade de atributos que essa classe possuir, ficará inviável passar todos como parâmetros no construtor. No caso poderia usar um objeto como parâmetro, mas se for do tipo da classe ele será inútil, porque assim que eu popular o objeto que é parâmetro do construtor, já estaria passando esses valores para os atributos via property´s e não precisaria passá-lo para o construtor. Desculpe se ficou confuso a minha dúvida.
Olá, Aleandro, bom dia!
Obrigado pelo feedback sobre o artigo.
Construtores são casos um pouco particulares nessa questão de parâmetros. Uma das formas é passar um objeto no construtor de uma classe, usando-o para alimentar as variáveis internas:
Outra forma – talvez mais adequada por deixar bem explÃcito os valores que a classe exige – é usar propriedades, preenchendo-as no chamador:
O que deve-se evitar sempre que possÃvel é criar objetos dentro do construtor, já que aumenta o acoplamento entre classes.
Abraço!