Refatoração não é só mudar o código!

Você provavelmente já ouviu alguém falando em refatoração, mas será que a pessoa realmente estava falando de refatoração? Qual a diferença entre refatorar e apenas modificar o código? Se quiser ir mais afundo no tema e entender como Padrões de Projeto podem ajudar, leia o livro Refatorando com Padrões de Projeto.

É importante entender a diferença, apesar de parecer algo mais conceitual, pois a mentalidade ao refatorar o código é diferente de quando implementamos uma funcionalidade nova ou estamos corrigindo um problema.

Ao refatorar buscamos maneiras de (1) melhorar o design existente, (2) aplicando mudanças em pequenos passos e (3) evitando deixar o sistema quebrado. Vamos falar sobre essas três partes em mais detalhes.

Ao buscar maneiras de melhorar o design existente devemos pensar em novas soluções que podem melhorar o código (um pensamento bem diferente de quando estamos corrigindo um bug).

A segunda parte é muito importante pois ao aplicar mudanças em pequenos passos garantimos a consistência e ritmo de desenvolvimento. Quanto menos mudanças fizermos mais fácil é evitar problemas.

E a terceira parte é bem ligada a segunda pois, ao fazer mudanças pequenas, podemos executar todos os testes e validar o que está sendo feito. Se o tempo entre ter testes verdes for muito grande, é um sinal de que talvez as mudanças devam ser menores.

As técnicas de refatoração descritas por Martin Fowler seguem esse modelo pois descrevem um passo-a-passo onde aplicamos uma mudança pequena e executamos os testes até alcançar o objetivo final da refatoração.

Se você quer refatorar o código o primeiro passo deve ser identificar o code smell e qual ação quer tomar.

Digamos que você encontrou um método que tem muitos argumentos e quer pensar numa maneira de melhorá-lo. Você pode criar um novo objeto para armazenar esses dados e passar apenas esse objeto, outra opção seria quebrar o método e dividir a lógica e parâmetros, ou ainda mover alguns desses parâmetros para um construtor.

Existem várias opções ao refatorar um código então é muito importante entender o contexto do código e decidir o caminho antes de começar as modificações.

Em seguida vamos aplicar a refatoração, sempre lembrando de fazer mudanças pequenas e garantir que os testes passem. Se no meio de uma refatoração você encontrar uma oportunidade de corrigir um problema, anote em algum lugar e continue. Lembre-se que queremos diminuir a quantidade de mudanças.

Uma vez que a refatoração foi aplicada podemos criar um commit pois deixamos o sistema com todos os testes passando. Agora podemos voltar ao modo de corrigir bugs ou implementar funcionalidades.

Nos próximos posts vamos ver como Padrões de Projeto podem nos ajudar na hora de decidir o caminho das refatorações. Até lá.

Anúncios

Como você organiza seu código?

Muitas práticas de programação tem como objetivo principal facilitar a leitura do programa, uma delas é a organização de código. Toda linguagem tem alguma forma de empacotar e dividir a lógica de negócio, mas que ao final não fazem tanta diferença assim na execução do programa.

Apesar de parecer ser uma atividade simples, organizar o código é algo que eu nunca havia pensado com muitos detalhes antes, até que um amigo me apontou para um artigo que apresenta quatro estratégias para organizar seu código.

Na grande maioria dos códigos em que trabalhei, a divisão mais comum é a por tipo, que organiza os pacotes baseado na função que cada classe desempenha. Aplicações Rails por exemplo são automaticamente geradas seguindo esse paradigma.

Captura de Tela 2016-05-21 às 6.56.39 AM

Todos os modelos ficam em uma pasta dedicada, todos os arquivos de configuração ficam em outra. No entanto o que isso te diz sobre o domínio da sua aplicação? Será que a organização das pastas deveria te dizer algo sobre o domínio? Recomendo assistir a apresentação Architecture the Lost Years, de Bob Martin, para ver mais detalhes sobre esse ponto.

Não é que essa organização esteja errada, o programa continuará funcionando normalmente e desenvolvedores conseguem entender a arquitetura e navegar pelo o código. O problema é pensar que essa é a única maneira de organização!

Na maioria dos projetos com frameworks Java que já vi é comum encontrar divisão por pacotes que não agrega nenhuma informação lógica – mesmo não sendo frameworks com convenções tão forte como Rails.

Por exemplo, todas as classes ficam dentro do pacote br.com.empresa.nomeDoProjeto, o único conteúdo do pacote br é o pacote com, que também tem como único conteúdo o pacote empresa… até que, depois de vários pacotes “vazios” chegamos ao código real.

O problema da má organização de pacotes é que isso pode forçar a complicar mais os nomes das classes. Tanto que existe um jogo Java: Real or Not? onde precisamos escolher qual classe é real do framework Spring entre outras duas que são inventadas.

Mesmo em projetos que usam frameworks com forte convenção, como Rails, é possível adaptar o código para que ele seja organizado de uma maneira diferente, como mostrado no post Baking a Cake with Rails.

Se você prefere organizar pacotes por componentes, por camadas ou qualquer outra forma, é possível! O objetivo é tornar a arquitetura e design mais explícitos, não seguir cegamente um framework, como descrito no post Screaming Architecture.

Como falei no começo, organização de código é algo que só comecei a me preocupar de verdade recentemente, mas já pude ver que, ao utilizar uma abordagem de divisão por componente o código fica bem mais conciso e fácil de navegar.

E você, já tinha parado pra pensar em como organiza seu código antes? Qual sua maneira preferida?

Porque nomear um atributo como “type” não é uma boa ideia em Rails.

Ou: como utilizar o padrão Herança de Tabela Única (Single Table Inheritance) em Rails.

Se alguma vez você já tentou criar um atributo chamado “type” no seu modelo Active Record, provavelmente você teve algum problema, ou já sabia exatamente o que estava fazendo. Se você se encaixa no primeiro grupo, então esse post talvez possa lhe ajudar um pouco.

Um pequeno exemplo

Vamos criar um scaffold usando Rails da seguinte maneira:

rails generate scaffold Dealer name type company

Criamos o Dealer (Negociante) que no nosso dominio tem os atributos nome (nome da pessoa) e empresa (empregador). Adicionamos também um campo type, esse campo deve ter os valores “Seller” caso seja um vendedor ou “Buyer” caso seja um comprador.

Agora vamos no rails console para criar um novo Dealer da seguinte maneira:

Dealer.create name: "Zeca", company: "Uburu Inc", type: "Seller"

A seguinte exceção será lançada: ActiveRecord::SubclassNotFound: Invalid single-table inheritance type: Seller is not a subclass of Dealer.

O começo da mensagem diz que o tipo da herança de tabela única é invalido pois a classe Seller não é uma subclasse de Dealer. Vamos ver um pouco mais sobre o padrão Single Table Inheritance (STI) [1]

Single Table Inheritance

O padrão STI está catalogado como parte dos Padrões de Arquitetura de Aplicações Empresariais (Patterns of Enterprise Application Architecture). O cenário de aplicação do padrão é quando você precisa representar uma herança em uma base de dados relacional.

A ideia para resolver o problema é bem simples, vamos adicionar um campo que vai dizer qual subclasse deve ser instanciada com aqueles dados. Voltando ao exemplo do post, podemos então ter uma superclasse chamada Dealer e duas subclasses Seller/Buyer que vão ter os mesmos atributos, mas lógicas diferentes.

O Rails já vem pronto para utilizarmos o STI. Basta utilizar que chamemos o campo que diferencia a classe de ‘type’. Por isso, no exemplo anterior, aquele exceção foi lançada. Criamos o modelo Dealer, mas ainda não temos o modelo Seller ou Buyer.

Para resolver o problema vamos criar o Seller e Buyer e fazer com que elas herdem de Dealer. Note que as classes Seller e Buyer não precisam herdar da classe ActiveRecord pois elas vão herdar de Dealer. O código seria algo do tipo:

class Seller < Dealer
end

Agora, se tentarmos criar o mesmo objeto de antes, veremos a seguinte saida:

Dealer.create name: "Zeca", company: "Uburu Inc", type: "Seller"
=> #<Seller id: 1, name: "Zeca", type: "Seller", company: "Uburu Inc", created_at: "2013-11-14 00:51:55", updated_at: "2013-11-14 00:51:55">

Ou seja, mesmo criando um Dealer, o Rails sabe que se trata de um Seller, graças ao STI. Se tentarmos criar um Seller também conseguimos o mesmo resultado:

Seller.create name: "Zeca", company: "Uburu Inc"
=> #<Seller id: 2, name: "Zeca", type: "Seller", company: "Uburu Inc", created_at: "2013-11-14 00:57:36", updated_at: "2013-11-14 00:57:36">

Veja também que o Rails já colocou o valor do atributo ‘type’ para “Seller” automaticamente. Assim você não precisa se preocupar em sempre criar um “Dealer” com o ‘type’ sendo “Seller” ou “Buyer”, basta utilizar a classes que você realmente precisa.

Se o nosso objetivo fosse só ter um campo chamado ‘type’ mas sem nenhuma herança podemos simplesmente renomear o campo para ‘alguma_coisa_type’ ou, se isso não for possível, podemos adicionar a seguinte linha na implementação da classe:

class Seller < ActiveRecord::Base
  self.inheritance_column = nil
end

Assim, o Rails não vai mais se importar com o ‘type’ para herança. Ou caso você precise configurar o atributo que define a herança como outro nome, também é possível passar o nome desejado para que o Rails trate o modelo como uma implementação do STI.

Referências

1. Martin Fowler, Single Table Inheritance. http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html

Cuidado ao cortar sua arquitetura

Quando estamos modelando o nosso sistema, é bem comum pensarmos em camadas. Talvez um dos modelos de maior sucesso no mundo do desenvolvimento (web ou não) é o MVC. A separação em camadas é bem interessante e facilita bastante a separação de responsabilidades e definição de limites.

No entanto, por mais lógico que pareça separar o desenvolvimento por camadas, dificilmente ajuda. Imagine a seguinte situação: uma nova funcionalidade precisa ser desenvolvida, pra simplificar pense num CRUD qualquer. Como você dividiria essa tarefa entre as pessoas da sua equipe?

Cortes horizontais

Dividir a tarefa em camadas, onde um par vai trabalhar no modelo (provavelmente mudando o banco de dados) e outro par fazendo a parte de views (fazendo algo na interface), pode parecer a decisão correta, afinal você pode colocar o especialista em banco de dados pra fazer a parte de modelos e outra pessoa que detona em CSS pra construir uma boa interface.

O problema dessa divisão é que ela exige uma sincronia muito grande entre a equipe. E (aqui vem um dos piores males do desenvolvimento de software) abre um espaço gigantesco para suposições.

O par que está desenvolvendo o back-end precisa construir tudo o que o par que vai trabalhar na interface vai precisar. E se a comunicação não acontecer, muita coisa pode ser feita a mais ou a menos. Esse é o maior perigo quando são feitos cortes horizontais na arquitetura.

Cortes Verticais

Agora imagine que, ao implementar a mesma funcionalidade de CRUD, vamos dividir as tarefas em ações. Então um par faz a parte de criação, por exemplo, e outro faz a visualização.

Obviamente a comunicação precisa acontecer para garantir que tudo seja feito de maneira homogênea, os nomes das tabelas são os mesmos, as views seguem os mesmos estilos, etc. Mas a comunicação sempre precisa acontecer, esse é o real segredo de projetos bem sucedidos!

No entanto, o par que desenvolve a visualização das entidades sabe exatamente o que vai ser necessário nos modelos para que as views funcionem. Assim, minimizamos o espaço para suposições, pois cada par pode implementar exatamente o necessário, nem mais nem menos.

Se eu não escrevi, então é legado.

Muita se fala sobre código legado, sobre práticas pra gerenciar código legado, como evoluir uma base de código legado, mas você já parou pra pensar o que é código legado?

Alguns dias atrás tivemos uma discussão com os membros da equipe utilizando como base o famoso livro “Working Effectively With Legacy Code” de Michael Feathers [1], e antes de entrar no livro eu propus que discutíssemos o que é código legado e chegamos a um conjunto de pontos interessantes sobre código legado.

Subjetividade

Michael Feathers define código legado como código que não possui testes [2], no entanto esta definição é um pouco antiga, pois o livro é de 2004, quando as práticas de testes automatizados ainda estavam sendo difundidas pela comunidade e as ferramentas começaram a aparecer.

O ponto de vista extremo do autor ressalta o fato de que código legado é um código que é difícil de ser mantido. Um código que não possui uma boa suíte de testes dificulta a análise do impacto de suas mudanças, e isso contribui para a inércia da equipe, que evita ao máximo realizar alterações profundas.

No entanto, hoje em dia com a grande abrangência e utilização de ferramentas de testes, não podemos confiar apenas nesse fato, pois um outro conjunto de fatores pode contribuir, talvez não com a mesma intensidade, para que a equipe sofra com mudanças.

É difícil escrever uma frase que defina totalmente o que é código legado, vamos então analisar um conjunto de pontos que podem facilitar essa análise e reflexão sobre o que um projeto legado.

Tecnologia

A tecnologia utilizada em um projeto modifica bastante o desempenho da equipe e sempre discutimos quais as melhores de acordo com cada situação. Com relação a código legado, será que a tecnologia faz um código ser legado ou não?

Os sistemas de pagamento que são escritos em cobol, podem ser considerado sistemas legados? Nesse caso a tecnologia não é mais presente no cenário atual de desenvolvimento, a dificuldade para manter esse tipo de sistema é muito grande, principalmente para encontrar desenvolvedores com conhecimento sobre a tecnologia.

Considere então um sistema que utiliza uma linguagem atual, mas que faz uso de bibliotecas, gems ou frameworks que não são compatíveis com novas versões. Nesse caso a tecnologia impede o aplicativo de continuar a evoluir e contribui para deixar a base de código legado, pois com a renovação da equipe fica difícil manter o conhecimento sobre versões anteriores.

No entanto a tecnologia não pode ser utilizado como único fator para indicar que o projeto é legado. Como um exemplo podemos analisar os aplicativos móveis que precisam manter compatibilidade com versões de sistemas anteriores. Apesar da tecnologia ser antiga o desenvolvimento continua ativo, apesar dos problemas com rotatividade de membros.

Manutenção

Isso leva a outro ponto interessante, o livro fala que código legado é código sem testes, mas qual é o papel da bateria de testes de um projeto? Talvez o mais importante seja facilitar a manutenção a longo prazo, ou seja, um código sem testes é muito difícil de ser mantido, caindo facilmente no limbo do código legado.

Um exemplo são sistemas antigos que ninguém mais altera com medo de fazer parar de funcionar. A solução na maioria dos casos é confiar que aquele monstrengo funciona e cercar ele de serviços que podem ser testados.

E quando a empresa não acredita em testes, então a linha que acaba de ser escrita já está legada? Não necessariamente, pois caso seja um sistema pequeno, onde é fácil entender e alterar (um CRUD simples, por exemplo), talvez os testes não sejam tão importantes.

Mas um sistema médio/grande que não possui nenhuma garantia de funcionamento está amaldiçoado a ser legado. Conforme o time for sendo renovado, os novatos precisarão de muita coragem e tempo de treinamento para realizar alterações, mesmo que sejam superficiais.

Abandonado

Passado algum tempo onde os desenvolvedores não tocam mais em uma determinada parte do sistema, ele começa a ficar abandonado. Um sistema, ou parte dele, que não é tocado por um longo tempo pode ser considerado legado?

Essa discussão leva a dois pontos, o primeiro é de que um desenvolvedor novo pode considerar o sistema legado e um mais experiente não. Isso geralmente acontece quando o conhecimento não está distribuído entre a equipe, então apenas uma pessoa do grupo fica “responsável” por atualizar aquela parte que realiza pagamentos.

O segundo ponto é de que o conceito de legado pode ser aplicado apenas em uma parte do sistema. No caso de sistemas realmente grandes pode ser que a utilização de determinada ferramenta que apenas uma pessoa da equipe conhece crie dificuldades para manter o sistema.

Idade

Um sistema velho pode ser considerado legado? A resposta depende do que é velho, que também é um conceito subjetivo. Um sistema de cinco anos que utiliza versões não tão atuais das tecnologias e que a equipe já foi completamente renovada, pode ser considerado legado?

É fácil perceber que não dá pra escrever um parágrafo que descreva o que é um sistema legado. A conclusão que nós chegamos foi de que, a partir de uma lista de conceitos relacionados a código legado podemos analisar se o nosso projeto é legado ou não. E essa discussão foi muito positiva, pois tratou principalmente sobre a confiança dos membros da equipe em alterar o sistema.

E o que é código legado para você? O quão confiável está você e sua equipe com o sistema que vocês desenvolvem?

Referências:

[1] FEATHERS, Michael C. Working effectively with legacy code.
[2] http://en.wikipedia.org/wiki/Legacy_code

[Livro] The Productive Programmer

The Productive Programmer

The Productive Programmer

O livro parte de uma premissa, no mínimo interessante: com o passar do tempo, o autor percebeu que os programadores ficaram MENOS eficientes, e não o contrário. A ideia é que, por passar a utilizar cada vez mais interfaces gráficas ao ponto de se interessar apenas por interface, o que seria um aumento de produtividade, do ponto de vista do usuário final de computadores, acabou prejudicando os desenvolvedores que perderam um pouco eficiência máxima do computador. No entanto, desenvolvedores são um tipo bem diferente de usuários de computador e muitas vezes precisam abrir mão da “facilidade de uso” para ter um ganho com eficiência.

A partir daí o autor parte para quatro princípios para se tornar um programador produtivo: Aceleração, Foco, Automação e “Canonicalidade”.

Aceleração fala sobre executar tarefas que fazemos a todo momento de maneira mais rápidas. Foco se refere a como alcançar e se manter em um estado de super produtividade, utilizando programas para reduzir distrações, procurar informações de forma mais eficiente, etc. Automação é auto-explicativo, se refere a deixar o computador trabalhar por você. “Canonicalidade” diz respeito a evitar duplicações de informações, para que você evite desperdício com redundância.

Esta primeira parte do livro realmente se refere a ferramentas que você pode utilizar para se tornar um programador mais produtivo. Coisas simples como utilizar um GnomeDo ou QuickSilver até a utilização de repositórios centralizados para compartilhar informações. É uma etapa meio maçante, mas que lhe apresenta informações interessantes.

Já a segunda parte do livro fala sobre a experiência do autor como consultor da ThoughtWorks em vários diferentes projetos e práticas que ajudam qualquer time de desenvolvimento a se tornar mais produtivo.

Um dos primeiros tópicos abordados é o TDD, falando sobre os benefícios, como evoluir códigos de teste e a importância da cobertura de código. Outro tópico fala sobre análise estática, em linguagens estáticas lógico, citando algumas ferramentas para análise de qualidade de código. Também são mencionados ferramentas para linguagens dinâmicas.

Outro tópico interessante discutido no livro é o principio “Good Citizenship “, que já foi falado aqui no blog (https://brizeno.wordpress.com/2012/11/26/o-objeto-bom-cidadao-good-citizenship/). Também são feitas discussões filosóficas, utilizando assuntos não relacionados com desenvolvimento de software mas que servem como exercício para uma discussão sobre princípios de design de software.

As metáforas utilizada no livro são bem interessantes, por exemplo quando o autor compara projetos de software onde sempre se busca adicionar informações ao navio Vasa, que foi um navio “inovador” com um orçamento infinito, mas que não conseguiu navegar mais que 120 metros.

Outro exemplo interessante é quando o autor fala sobre desenvolvedores que tem medo de tentar novidades porque todos os outros dizem que algo “sempre foi feito assim”. A comparação é feita com o experimento dos macacos raivosos, onde um grupo de macacos era colocado em uma sala com uma escada e um cacho de banas no final da escada. Sempre que um dos macacos subia, os outros levavam uma rajada de água gelada. Com o tempo, os macacos começaram a bater em qualquer outro macaco que tentava subir. Com o tempo, nenhum macaco subia mais, então um dos macacos foi substituído, e logo o macaco novo tentou subir a escada e apanhou dos outros, até desistir de subir. Os macacos foram então substituidos por completo e chegou-se ao ponto que nenhum macaco tentava subir sem nunca ter sido molhado.

No fim, o livro vale muito a pena. Os capítulos são curtos e bem concisos, ou seja, nada de redundância ou conversa fiada para deixar o livro maior. Então se você quiser se tornar melhor no que faz todo dia, recomendo a leitura do livro. Não sei sobre detalhes da versão traduzida, mas a versão original utiliza uma linguagem bem simples e fácil de ler.

The Productive Programmer
By Neal Ford
Publisher: O’Reilly Media
Released: July 2008
Pages: 226

O “objeto” bom cidadão (Good Citizenship)

Um tópico bem falado quando o assunto é design de software é sobre quebra de encapsulamento e como projetar bem as classes para que elas sejam altamente coesas (tenha um objetivo bem definido) e pouco acopladas (dependam de poucos objetos para funcionar).

Quando se fala sobre relacionamentos entre objetos, um bom tópico para ser estudado é a “boa cidadania” ou Good Citizenship.

Good Citizenship

Um livro que aborda bem o assunto é “The Productive Programmer” [1], de Neal Ford. Nele o autor aborda alguns pontos chaves de maneira bem simples e com exemplos para mostrar o porquê de tamanha preocupação com esses pontos.

Segundo Ford [1], a boa cidadania se refere a objetos que são conscientes do seu estado e atentos aos estados dos outros objetos. Quando se fala em estado de um objeto, significa, de maneira bem simples, as suas variáveis internas.

As variáveis internas de um objetos definem como ele se comporta, geralmente as computações feitas em métodos dos objetos dependem das suas variáveis. Elas também representam o relacionamento do objeto com os outros, indicando as suas dependências. Daí a preocupação com o estado do objeto.

Quebrando a encapsulação

Um dos exemplos mais simples de quebra do princípio da boa cidadania é a quebra de encapsulação. Um post bem conhecido do Paulo Silveira no blog da Caelum [2] fala justamente sobre isso, tanto que o título é “Como não aprender Java e Orientação a Objetos: getters e setters”. Ao invés de copiar e colar o texto aqui, o link está no final do post, leitura obrigatória.

Esse ponto já foi exaustivamente discutido e todo mundo sabe o quão ruim pode ser oferecer getters e setters para todos os atributos dos objetos. Quando vários objetos tem acesso a uma determinada informação fica mais difícil mudar essa informação. E como nós todos sabemos, quase nunca é necessário mudar uma informação em um projeto de software.

Construtores

Esse é um tópico interessante que, eu particularmente, ainda não tinha ouvido falar antes de ler o livro [1]. Como o construtor pode quebrar o princípio da boa cidadania? Como o construtor pode mudar o estado do objeto? É justamente ai que Ford ataca a maneira como os construtores são vistos.

Segundo Ford [1], os construtores são geralmente vistos apenas como um mecansmos para criar objetos, no entanto os construtores dizem para você o que é necessário para que um objeto seja criado com um estado válido. Então qual é o ponto de utilizar um construtor vazio? Se o estado inicial do seu objeto não precisa de nenhuma informação, então tudo bem, mas dificilmente esse é o caso.

Remover os construtores padrões é bem difícil dependendo da linguagem e dos frameworks utilizados. Em C++ existe sempre um construtor padrão que não necessita de argumentos, pode ser por não passar nenhum argumento, ou por utilizar valores padrões para todos eles. Ford [1] também fala sobre a especificação do JavaBeans, que obriga a utilização de um construtor padrão vazio.

Se a linguagem/framework necessitar de construtores padrões é bem difícil que você consiga ir contra, a menos que possa ser substituido por algo diferente. A recomendação é que os construtores padrões sejam tratados evitados pela equipe.

Métodos estáticos

Os métodos estáticos tem como função funcionar como se fossem funções (???), ou seja, eles são utilizados de maneira procedural, sem utilizar nenhum estado ou guardar nenhuma informação dos objetos e de si mesmo. Um bom exemplo são as classes que são responsáveis por cálculos matemáticos, você chama o método sem precisar instanciar ou se preocupar com o que vai acontecer depois.

O grande problema quando um método estático guarda informações é que, diferente de um objeto que é instanciado dentro de um método, o escopo de um método estático é, muitas vezes, todo o programa. Então não existem garantias de controle do estado do objeto, uma vez que qualquer outro lugar do código pode alterá-lo caso necessário.

Esse é o mesmo problema com variáveis globais e com a versão “orientada a objetos” de variáveis globais, também conhecida como Singleton [3]. Um dos problemas com o singleton é que facilmente ele se carrega com várias responsabilidades, pois como você não tem como controlar o estado do objeto, a classe acaba se carregando de responsabilidades para garantir que as computações sejam feitas por completo. E então acaba se relacionando cada vez mais com outros objetos e aumenta a complexidade e no final você fica com outra classe Main e ainda tem a coragem de dizer que usa padrões de projeto orientado a objetos.

No entanto o padrão Singleton não é sinônimo de gambiarra (tá, é sim), o ponto é só lembrar que ele não deve se carregar com responsabilidades. Tente extrair as responsabilidades do objeto utilizando outros padrões de projeto. No entanto esteja atento que é dificil garantir controle sobre o objeto, então pense duas, três ou quantas vezes forem necessárias para se convencer a não utilizar um Singleton.

Utilizando um objeto que não é um bom cidadão

Um último ponto abordado em The Productive Programmer [1] sobre boa cidadania é sobre utilizar um objeto que não é um bom cidadão. O autor cita como exemplo a classe mais odiada do universo: java.util.Calendar.

Além de ferir o senso comum de qualquer pessoa (que os meses NÃO começam em zero), o autor cita como um dos principais problemas o fato de que o Calendar não é capaz de manter controle sobre seu próprio estado. Como por exemplo quando você utiliza os horríveis setters para colocar a data como 31 de Fevereiro. O que deveria acontecer? Um exceção? Não atualizar a informação? Não, é claro que quando você seta a data para 31 de Fevereiro, na verdade você quis dizer 2 de Março =]

A opinião de Ford é: remova o objeto “criminoso”. No caso da Calendar é possível utilizar a biblioteca Joda Time [4] como foi muito bem exemplificado no post do Rafael Lacerda no blog da Caelum [5] falando sobre a dificuldade de cálculos de datas, leitura obrigatória também.

No entanto podemos tomar outras atitudes, caso o objeto não possa ser removido. Talvez a melhor seja encapsular esse objeto dentro de uma outra classe que oferece um maior controle. Um padrão de projeto para isso é o Adapter [6], também conhecido como Wraper. Com ele você pode ter uma instância do objeto criminoso dentro de uma classe que vai oferecer métodos mais amigáveis e garantir que ele se comporte.

Apesar de simples, e até algumas vezes óbvio, é difícil seguir este princípio. No entanto vale a pena “perder” um pouco de tempo pensando sobre o design das suas classes e como elas funcionam atualmente. Como diria Maurício Aniche [7], também da Caelum, você não vai arder no inferno ágil se pensar um pouco antes de escrever testes para esperar o “design emergir”.

Referências:

[1] Neal Ford, The Productive Programmer. O’Reilly, 2008.
[2] Paulo Silveira, Como não aprender Java e Orientação a Objetos: getters e setters. (http://blog.caelum.com.br/nao-aprender-oo-getters-e-setters/)
[3] Marcos Brizeno, Mão na massa: Singleton (https://brizeno.wordpress.com/category/padroes-de-projeto/singleton/)
[4] Joda.org (http://joda-time.sourceforge.net/)
[5] Raphael Lacerda, O eterno problema de calcular a diferença de dias entre duas datas em Java. (http://blog.caelum.com.br/o-eterno-problema-de-calcular-a-diferenca-de-dias-entre-duas-datas-em-java/)
[6] Marcos Brizeno, Mão na massa: Adapter. (https://brizeno.wordpress.com/category/padroes-de-projeto/adapter/)
[7] Maurício Aniche, Métodos ágeis: o que é folclore e o que é real? (http://www.infoq.com/br/presentations/agile-realidade-folclore)