Em busca do nome adequado: métodos, variáveis e classes
Postado em 08. out, 2010 por Guilherme Silveira em Agile, Arquitetura
É muito comum após alguns dias de trabalho em um projeto perceber que as escolhas de nomes de classes e métodos não condizem com o que cada um representa. Isso acontece pois, com o passar do tempo, aumenta o nosso conhecimento sobre o domínio do problema.
Também é natural surgir o desejo de mudança: a medida que nos especializamos nesse domínio, o modelo do negócio em nossa mente passa a ser diferente da visão inicial.
Um exemplo simples dessa mudança acontece quando em OO criamos um relacionamento bidirecional entre duas classes através de listas, como no exemplo a seguir:
// a representação de um cliente, funcionário etc
public class Pessoa {
List<Conta> contas;
}
// a representação de uma conta bancária (poupança, corrente, investimento etc)
public class Conta {
List<Pessoa> pessoas;
}
A primeira vista esse é o relacionamento entre as duas entidades mas em determinado momento notamos que na verdade a relação entre pessoa e conta é um Titular, que possue características próprias, como o contrato que ele como titular da conta assinou:
public class Pessoa {
List<Titular> titularidades;
}
public class Conta {
List<Titular> titulares;
}
public class Titular {
Conta conta;
Pessoa pessoa;
// outras características de uma titularidade
}
Nesse caso o processo de refatoração envolve extrair uma classe que representa tal relação, um Titular: um exemplo simples que demonstra como em OO é comum a existência de classes para representar informações intangíveis.
Outras questões simples mas fundamentais para o bom design e legibilidade da aplicação estão ligados aos nomes que escolhemos, não só em relação ao nome da classe e de seus métodos. No exemplo acima, por uma escolha fraca no nome da variável membro conta e pessoa acabamos com um código que parece repetitivo e pouco descritivo:
Pessoa pessoa;
Se em nosso sistema funcionários, gerentes, titulares e caixas são representados através de instâncias do tipo Pessoa, o mais adequado seria nomeá-los de acordo:
public class Titular {
Conta conta;
Pessoa cliente;
}
A tendência natural do desenvolvedor em linguagens que obrigam a declaração do tipo é criar variáveis com nome identicos ao tipo declarado, por ser um atalho das IDEs e mais simples de escrever. Mas tais alternativas, como visto, dizem pouco sobre a variável que está sendo acessada. Por esses motivos, é muito comum encontrar código como:
Cliente cliente = clienteDao.busca(13);
Pagamento pagamento = pagamentoDao.busca(15);
ProcessadorDePagamento processador =
new ProcessadorDePagamento();
processador.processa(cliente, pagamento);
Cliente, pagamento, processador: é somente desses três elementos que nosso domínio é composto? E nem mesmo Ruby foge disso, sendo comum encontrar:
cliente = Cliente.find_by_id 13 pagamento = Pagamento.find_by_id 15 processador = Processador.new processador.processa cliente, pagamento
Três aparições de cliente, três de pagamento, quatro de processador. Aqui existe uma visão distorcida de que variáveis são somente aquilo que o tipo descreve: um cliente, um pagamento, um processador. Na verdade elas referenciam instâncias específicas e especializadas de um tipo determinado. Só faria sentido chamar o cliente 13 de cliente se ele é o único cliente, um singleton. Especialize o nome de suas variáveis e crie um código mais legível:
Cliente devedor = clientes.busca(13);
Pagamento fatura = pagamentos.busca(15);
ProcessadorDePagamento mensalidade =
new ProcessadorDePagamento();
mensalidade.cobra(devedor, fatura);
O mesmo pode ser refletido no exemplo de Ruby. E como trabalharemos nossos repositórios para permitir uma maior legibilidade do código?
Pessoa pessoa = ...; List<Conta> contas = dao.contasDe(pessoa);
Note como por causa da escolha do nome da variável pessoa fomos obrigados a colocar algo mais descritivo no método. Caso a variável possua um nome mais adequado, teríamos então:
Pessoa cliente = ...; List<Conta> contas = dao.contasDe(cliente);
Modificando nossa classe Conta para adicionar o gerente e o subgerente temos novamente o sentido das variáveis descrito não só pelo seu tipo, mas também pelo seu nome:
public class Conta {
List<Titular> titulares;
Pessoa gerente;
Pessoa subgerente;
}
Nesse instante a query ficaria mais complexa pois existem dois relacionamentos distintos com pessoa, requerendo a criação de uma DSL interna para query. No exemplo a seguir usamos um builder para facilitar a construção da query:
Pessoa gerente = ...;
Pessoa subgerente = ...;
List<Conta> contas =
dao.comGerenteESubgerente
(gerente, subgerente):
Essa é a abordagem que o jbehave 3.2 e o selenium utilizam, onde acabamos por ter uma sintaxe muito longa e repetitiva.
Retomando o problema do nome das variáveis, que pode ser resolvido com outra refatoração de rename: o código da terceira linha é mais descritivo que o anterior:
Pessoa gerente = ...;
Pessoa subgerente = ...;
List<Conta> contas =
dao.comGerente(gerente)
.comSubgerente(subgerente):
Em linguagens onde mapas podem ser representados através de literais, o ruído sintático é baixo e permite que seja escrito código como a seguir em Ruby:
gerente = ...
subgerente = ...
contas = Conta.com
:gerente => gerente, :subgerente => subgerente
Novamente, a refatoração aqui é fundamental:
principal = ...
secundario = ...
contas = Conta.com
:gerente => principal, :subgerente => secundario
A escolha dos nomes de nossas classes influencia o design e legibilidade de nosso código direta e indiretamente, primeiro pela declaração das variáveis e a nossa visão do domínio. Depois pelas consequências que o mesmo possui na escolha dos nomes das variáveis e métodos, que permitem um design mais ou menos legível.
Essa escolha é tão importante quanto o resto de seu design para facilitar a compreensão do que seu domínio é e qual o problema que está tentando atacar. Na próxima vez que for declarar uma variável com o mesmo nome que o tipo, pense duas vezes.
15 Respostas para “Em busca do nome adequado: métodos, variáveis e classes”
Trackbacks/Pingbacks
-
-
maio 12, 2011
[...] Referente a esse problema, indico um excelente post do Guilherme Silveira: http://blog.caelum.com.br/em-busca-do-nome-adequado-metodos-variaveis-e-classes/ [...]
-
-
julho 29, 2011
[...] de : http://blog.caelum.com.br/em-busca-do-nome-adequado-metodos-variaveis-e-classes/ Share on Facebook Tweet by leonardopcs | Leave a comment | Uncategorized ← The Social [...]
ASSINE NOSSO RSS




Tiago Farias
08. out, 2010
Excelente post! Estava lendo sobre essa assunto no tectura essa semana… Essa coisa de declarar a variável com o mesmo nome do tipo parece mesmo uma doença! Esses posts e discussões são a cura pra isso =]
Cada dia mais penso em como deixar o meu código o mais fluente possível. Keep it up!
Rafael Ponte
08. out, 2010
Excelente post, Guilherme.
Dar nome à classes, métodos e variáveis nem sempre é fácil, principalmente quando se trabalha com um domínio complexo e há muitos ruídos na comunicação.
Porém mais importante que dar nomes apropriados aos artefatos no software é mante-los atualizados (em sincronia) com o entendimento do negócio e a linguagem ubíqua utilizada pela equipe. O que tenho notato é que muitos desenvolvedores preferem ter o desgate e ruído na comunicação a ter que alterar (refatorar) código para manter a sincronia com o negócio, e esquecem, ou não sabem, que este problema na comunicação só tende a piorar com o tempo.
Enfim, este é um assunto que sempre me interessou muito e este post é altamente recomendado.
Felipe Affonso
08. out, 2010
Muito legal, parabéns.
Legal desse tipo de post, por não ser mais um “TUTORIAL DE FRAMEWORK X”.
Impressionante o jeito como vocês conseguem evoluir os códigos, práticas internas da Caelum, e por consequência levam toda a comunidade a oportunidade de evoluir junto. Seja pelos treinamentos pagos, seja pelas iniciativas gratuitas como por exemplo esse blog, entre outras.
Li as apostilas do FJ-11, FJ-28, e outras… muito me ajudaram no início de minha jornada. Fiz curso de arquitetura e design na Caelum, e sigo aprendendo com vocês.
Obrigado pela iniciativa!
Daniel
09. out, 2010
Excelente post!!
Parabéns pra Caelum por compartilhar conhecimento, já evolui muito como
profissional com aprendizado que adquiri neste blog
Guilherme Silveira
10. out, 2010
Obrigado Felipe, Tiago e Daniel!
Pois é Rafael, essa sincronia que é fundamental. Sabemos que o nosso conhecimento do dominio melhora com o tempo, e a refatoração eterna garante a melhoria do código de acordo com essa melhora.
Abraços
Christian
19. out, 2010
Só uma observação Guilherme:
A refatoração:
List contas = dao.comGerente(gerente).comSubgerente(subgerente):
Acaba por criar um problema: a chamada a dao.comGerente(gerente deveria retornar um List. Esta interface não permite a chamada a comSubgerente(subgerente).
Para fazer isso, eu deveria decorar o List com algum outro Objeto, certo?
(Estou assumindo que comGerente(gerente) e comSubgerente(subgerente) podem ser chamados independente da ordem, evitando o Sequential coupling).
[]´s
Guilherme Silveira
20. out, 2010
Oi Christian,
Perfeito: se a abordagem citada utilizar o retorno de comGerente de uma List de Java padrão, não temos a definição de comSubgerente. Por isso a criação de DSLs em linguagens com tipagem definida como no Java (forte e estática), precisamos tomar alguns cuidados.
Uma solução é fazer dao.comXpto() devolva a própria interface do dao (que deve ter um nome mais educado, como Contas). Então eu teria Contas.comGerente(x).comSubgerente(y) e o encadeamento é livre. Como Contas implementa a interface Iterable, também é possível iterar nela…
Outra opção é devolver uma List com métodos parciais, ou ainda um Set.
Apesar de devolver algo com a mesma interface, internamente os objetos são imutáveis e são sempre devolvidos novos objetos com a query parcialmente construida. Um exemplo disso em ruby é o relata: http://github.com/caelum/relata
O que acha?
Abraço!
Christian
21. out, 2010
Oi Guilherme.
Realmente Ruby é fantástico, vou analisar o relata com calma.
No caso do método comGerente(x) devolver devolver a interface do dao (Fluent Interfaces) eu acabo por misturar uma Interface de um Dao (afim de promover o encadeamento) com uma interface Iterable, para percorrer as contas. Me veio a mente ValueListHandler, mesmo não sendo bem isso.
Cada encadeamento acaba por ser um filtro da chamada anterior, sendo que a primeira chamada faz a busca no repositório de dados e as demais vão filtrando o resultado (e sendo encadeados), e então não tenho dependência na ordem em que eu chamo os métodos e mantenho o encadeamento.
É isso mesmo ou viajei?
[]´s
Guilherme Silveira
22. out, 2010
Tudo bem Christian?
É isso mesmo, um builder de filtros para fazer a busca! Olha o código do Relata que você vai ver isso acontecendo.
Abraço!
Vinicius Viana
27. out, 2010
Ótimo post, é muito comum pegarmos códigos como o exemplificado no cotidiano ;D
André Thiago
14. nov, 2010
Belo post!
No caso de dao.comGerente(gerente).comSubgerente(subgerente), essa implementação seria possível se o dao for stateless?
Imagine ainda se o dao for um bean gerenciado pelo Spring Framework, e este fornecer, via DI, a session do Hibernate. Se no retorno de comGerente(gerente) eu retornar uma nova instância do dao (dando um new, por exemplo) eu perderia o gerenciamento do Spring – e, por consequência, a DI.
Além do mais, eu não sei se o uso de fluent interface é adequado pada dao’s. Fazendo dao.comGerente(gerente).comSubgerente(subgerente) – corrija-me se eu estiver enganado – eu faço uma consulta em comGerente e depois outra consulta em comSubgerente. Ou seja, faço 2 acessos ao banco de dados onde apenas um resolveria.
Guilherme Silveira
16. nov, 2010
Tudo bem André.
Você está correto. Em implementações mais simples de DAO, isso poderia ficar sujinho ou até mesmo virar um caos. Mas isso é questão de bom design de DSLs e não do DAO em si:
Os dois exemplos que você citou:
bom design 1: seu objeto de busca (chamamos ele de DAO, poderia ser outra coisa) ser imutável.
Isso implica que a cada chamada de comXpto eu devolveria um novo objeto e não alteraria valores internos do atual
bom design 2: a busca não é feita até o dado ser usado
Isso implica que chamar comXpto não faz a query, so cria um novo objeto com o pedaço da criteria concatenado. Depois na hora de chamar size ou iterator, ele executa a query.
Abraço!
Escola de Poker
16. jul, 2011
Boa tarde:), o meu nome é Sofia estudo Engenharia Civil e adorei imenso da tua página! Muito bonito sim senhora!
Adequa-se muito bem tudo aquilo que aqui li.Hoje sempre muito para expressar nos blogues!Nada melhor do que implementar a nossa marca na net!
Até à próxima