Muitos parâmetros? Named Parameters em Ruby e Builders em Java

Pode ser difícil ler uma invocação de método ou de construtor quando temos muitos parâmetros. Isso piora se tivermos parâmetros do mesmo tipo. Um exemplo em Ruby:

Titular.new("Hugo Roque", "Josemar Nunes", "Antonio")

O que esse construtor recebe como parâmetros? O que esses nomes representam? Lendo simplesmente dessa forma, só podemos chutar. Há muitas formas de atenuar esse problema. Uma das soluções seria utilizar tinytypes, criando classes, como Dependente, que possuem um ou pouquíssimos atributos, servindo mais como uma forma de tipificar e explicitar com o que trabalhamos, evitando a “programação orientada a strings”. Em Java é comum utilizarmos o design pattern Builder, evitando construtores gigantes e complexos. Dessa forma o código acima seria algo como:

Titular hugo = new TitularBuilder()
    .nome("Hugo Roque")
    .dependentes("Josemar Nunes", "Antonia Roque")
    .build();

Em Ruby, é comum passarmos hashes como argumento, inclusive muitas gems que usamos no dia a dia utilizam essa técnica. Se estivermos usando o ActiveRecord e precisarmos procurar os 5 primeiros usuários por ordem de criação, podemos utilizar o seguinte código:

Usuario.find(:all, order: "created_at DESC", offset: 1)

Já no caso do nosso Titular, a chamada seria feita da seguinte maneira:

hugo = Titular.new(nome: "Hugo Roque", dependentes: ["Josemar Nunes", "Antonia Roque"])

Que já melhora bastante a leitura na chamada do método, mas piora a legibilidade do código que define o método:

class Titular
  attr_reader :nome, :dependentes

  def initialize(options={})
    @nome = options[:nome]
    @dependentes = options[:dependentes]
  end
end

Observe que não está claro na assinatura do método as informações que ele precisa, teremos que olhar a implementação para descobrir o que receberemos na hash. Além da expressividade, podemos deixar de passar um parâmetro e nada irá nos alertar, todos os parâmetros se tornam opcionais. O Ruby 2.0promete uma forma nativa de nomear os parâmetros, facilitando tanto a leitura quanto a escrita das invocações. Veja uma possível invocação que poderemos fazer no futuro:

class Titular
  def initialize(nome: "padrao", dependentes: [])
    @nome = nome
    @dependentes = dependentes
  end
end
hugo = Titular.new(nome: "Hugo Roque", dependentes: ["Josemar Nunes", "Antonia Roque"])

Isso trará muitas vantagens para a forma como desenvolveremos, porém a versão 2.0 do Ruby tem previsão de lançamento para fevereiro de 2013. Enquanto essa novidade não chega, algumas soluções provisórias foram desenvolvidas, eu mesmo implementei a gem named-parameterque disponibiliza uma forma de simular esses parâmetros nomeados de forma limpa e simples. Através da gem, invocamos o método como se estivéssemos recebendo uma hash, mas definimos da seguinte maneira:

class Titular
  extend NamedParameter

  named def initialize(nome, dependentes)
    @nome = nome
    @dependentes = dependentes
  end
end

E você, como evita uma grande quantidade de parâmetros?

10 Comentários

  1. José Dal Prá Junior 03/12/2012 at 11:02 #

    Como usamos Java, builders é a solução. Mas para economizar horas de trabalho usamos o lombok. Usamos uma extensão: http://peichhorn.github.com/lombok-pg/Builder.html.

    Ótimo post.

  2. Roberto Shizuo 03/12/2012 at 15:06 #

    parabéns! o builder no java salva nesse caso mesmo.

  3. Fred 03/12/2012 at 16:38 #

    Scala tambem possui named parameters.

  4. Hugo Roque 03/12/2012 at 16:53 #

    @José Opa, não conhecia esse lombok, vou dar uma olhada.

    @Fred Verdade, já consertei no post, obrigado 😉

  5. Fernando Mantoan 04/12/2012 at 19:52 #

    A solução de Builder realmente torna mais elegante e intuitiva a construção dos objetos, e também achei sensacional a nova abordagem do Ruby 2.0, e parabéns por sua iniciativa com a gem.

  6. Márcio Ferreira 05/12/2012 at 01:43 #

    Bom post! Vale lembrar que Joshua Block, no seu livro Effective Java, recomenda o uso de Builders para construtores com demasiados parametros, ao invés de termos construtores “telescopicos” ou java beans.

  7. Paulo Fernando Stoppa 06/12/2012 at 14:23 #

    O uso de Builders realmente é muito bom. Também tomo como base o Effective Java. E utilizando o padrão Builder, também consigo garantir que só serão instanciados objetos que satisfazem os critérios mínimos para a existência do mesmo.

  8. Victor Franz 13/12/2012 at 11:31 #

    No Groovy tb é suave a construção de objetos.

    Já em java, por que não simplesmente fazer os métodos setters retornarem ele mesmo? Por que não é padrão java bean?

  9. Paulo Fonseca 23/01/2013 at 17:15 #

    Sua gem é show de bola! Parabéns!

  10. Sdr 15/03/2013 at 13:29 #

    +1 “No Groovy tb é suave a construção de objetos.”

Deixe uma resposta