Em busca do nome adequado: métodos, variáveis e classes
É 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.