Tag: design patterns

Dissecando o padrão de projetos Singleton

singleton O padrão de projetos Singleton consiste em uma forma de garantirmos que teremos uma única instância de uma determinada classe no programa atual.

Por exemplo, em um programa Java para desktop podemos criar um Singleton da classe que gerencia a conexão com o banco de dados.

Este é um dos design patterns mais simples que existem, mas ele possui algumas nuances importantes de se entender do ponto de vista de implementação.

Implementação inicial em Java

Para garantirmos uma única instância de uma classe, a abordagem mais comum é criar um método estático que retorne sempre o mesmo objeto. Exemplo:

public class Singlegon {
    private static Singleton instance = new Singleton();
    public static Singleton getInstance() {
        return instance;
    }
    private Singleton() { }
}

O atributo estático instance armazena o objeto criado para retornar a cada chamada de getInstance().

O trecho private Singleton() { }; é um construtor privado, garantindo que nenhuma outra classe poderá criar inadvertidamente uma instância desta.

Postergando a criação do objeto

Tudo ok, mas nem sempre queremos criar o objeto no modo agressivo (eager), isto é, instanciá-lo logo que a classe é carregada. Em muitas situações, é desejável postergar a criação do objeto até a primeira chamada. Exemplo:

public class Singlegon {
    private static Singleton instance;
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

No código acima, o primeiro acesso ao método getInstance() irá disparar a criação do objeto, que será então retornada nas demais chamadas.

Problemas de sincronizção

O problema do código acima é que se houver mais de uma chamada concorrente no primeiro acesso ao método getInstance() ele pode criar duas instâncias de Teste. Duas threads poderiam entrar dentro do if, certo?

A solução mais básica para isso é sincronizar o método:

public static synchronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
}

O problema desta abordagem é que todas as chamadas estarão sujeitas a bloqueios, deixando a execução geral do programa mais lenta. Imagine um método assim num servidor de aplicação com vários usuários acessando o sistema! É terrível.

Uma solução melhor seria um bloco synchronized dentro do if, assim:

public static Singleton getInstance() {
    if (instance == null) {
        synchronized (Singleton.class) {
            instance = new Singleton();
        }
    }
    return instance;
}

Isso resolve o problema da sincronização em todos os acessos, mas é uma solução “ingênua”, pois na verdade voltamos ao problema inicial. Como o if não está sincronizado, duas threads diferentes podem entrar no bloco de criação ao mesmo tempo e, mesmo com a sincronização, elas retornarão instâncias diferentes quando instance == null.

Então, a solução mais “pura” para o singleton pattern seria acrescentar uma verificação dupla, assim:

public static Singleton getInstance() {
    if (instance == null) {
        synchronized (Singleton.class) {
            if (instance == null) {
                instance = new Singleton();
            }
        }
    }
    return instance;
}

Com esta última abordagem garantimos que não haverá perda de desempenho por causa de sincronização desnecessária do método inteiro.

Além disso, garantimos uma única instância de Teste, pois mesmo que duas chamadas concorrentes entrem dentro do primeiro if, temos uma nova verificação sincronizada.

No pior caso, se houver duas ou mais chamadas concorrentes no primeiro acesso a getInstance (quando INSTANCE ainda é null), apenas estas primeiras chamadas serão sincronizadas, sendo que após a primeira atribuição de INSTANCE, nenhuma chamada posterior será sincronizada.

Além do Singleton: padrão Registry

Alguns argumentam que o padrão Singleton está depreciado e deve ser abandonado. De certa forma eu concordo, pois em ambientes onde múltiplas threads e múltiplas aplicações executam concorrentemente, ter apenas um objeto quase nunca é desejável.

O padrão de projeto Registry permite armazenarmos uma coleção de objetos, cada um contendo um identificador ou um escopo específicos. É como se limitássemos o escopo do Singleton de “um objeto por programa” para “um objeto por qualquer escopo que quisermos“.

A implementação varia muito, mas podemos encontrar exemplos claros desse padrão em:

  • Threadlocal, que permite armazenar valores para uma thread, ou seja, um Singleton para cada uma.
  • HttpSession, que retorna sempre o mesmo objeto para cada usuário do sistema web, ou seja, um Singleton por usuário.
  • Frameworks de Injeção de Dependências como Spring ou CDI, os quais gerenciam a criação de objetos em diferentes escopos e permitem inclusive usar o padrão Singleton declarativamente.

Não entrarei em detalhes sobre o Registry neste artigo.

Indicações de Leitura

Uma leitura mais completa sobre o assunto está no Head First Design Patterns (Use a Cabeça! Padrões de Projeto). Embora tendo suas falhas, este é um livro muito bom para quem ainda está começando a entender Padrões de Projetos.

Outros detalhes interessantes sobre Singleton, como variações de implementação, podem ser encontrados na Wikipédia (em Inglês).

Uma breve definição do padrão Registry pode se encontra no catálogo de padrões do Martin Fowler.

Construindo objetos de forma inteligente: Builder Pattern e Fluent Interfaces

building-with-legosA Orientação a Objetos é constantemente mal utilizada. Sem o devido treino, temos a tendência de raciocinar de forma estruturada.

Esse problema está tão enraizado que muitas vezes encontramos dificuldades nos pontos mais fundamentais da programação, tal como a construção de instâncias de objetos um tanto mais complexos que beans simples. Como consequência, nosso código fica difícil de manter e mais propenso a erros.

Portanto, todo desenvolvedor deve estudar Padrões de Projetos (Design Patterns), já que estes ajudam a resolver os problemas mais comumente enfrentados e nos dão o treino necessário para pensarmos sobre os problemas mais complexos. Além disso, existem outras técnicas, como as Interfaces Fluentes (Fluent Interfaces), que ajudam a criarmos código mais legível e, dessa forma, aumentam a produtividade e diminuem a propensão a erros.

Criando um objeto (the dummy way)

Suponha que precisamos criar um objeto com diversos atributos “opcionais”. Vamos usar uma pizza como exemplo (um dos exemplos clássicos para ilustrar padrões de projetos). Já vi muitos construtores de “pizza” por aí da seguinte forma:

public class Pizza {

	private int tamanho;
	private boolean queijo;
	private boolean tomate;
	private boolean bacon;

	Pizza(int tamanho) {
		this.tamanho = tamanho;
	}

	Pizza(int tamanho, boolean queijo) { 
		this(tamanho);
		this.queijo = queijo;
	}

	Pizza(int tamanho, boolean queijo, boolean tomate) {
		this(tamanho, queijo);
		this.tomate = tomate;
	}

	Pizza(int tamanho, boolean queijo, boolean tomate, boolean bacon) {
		this(tamanho, queijo, tomate);
		this.bacon = bacon;
	}

}

Diga a verdade, você já faz isso em algum momento? E pode ficar pior se acrescentarmos construtores com combinações e ordenação diferentes de parâmetros!

A sobrecarga é interessante quando temos algumas poucas variações de parâmetros e há poucas mudanças no conjunto de atributos. Porém, chega uma hora que nem sabemos mais o que está acontecendo. Quanto tempo você já perdeu inspecionando conteúdo de classes de terceiros para entender que valores deveria usar? Exemplo:

new Pizza(10, true, false, true, false, true, false, true, false...)

Que tipo de pizza é essa mesmo? 😯

Criando um objeto com Builder Pattern

Alerta: antes que alguém fique agitado porque este exemplo diverge do Builder Pattern originalmente publicado no GoF, leia o livro com atenção, pois os próprios autores propõem implementações alternativas dos padrões de projeto e afirmam categoricamente que estes devem ser adaptados de acordo com o contexto. É sério, está escrito lá, acredite ou não.

public class Pizza {

	private int tamanho;
	private boolean queijo;
	private boolean tomate;
	private boolean bacon;

	public static class Builder {

		// requerido
		private final int tamanho;

		// opcional
		private boolean queijo = false;
		private boolean tomate = false;
		private boolean bacon = false;

		public Builder(int tamanho) {
			this.tamanho = tamanho;
		}

		public Builder queijo() {
			queijo = true;
			return this;
		}

		public Builder tomate() {
			tomate = true;
			return this;
		}

		public Builder bacon() {
			bacon = true;
			return this;
		}

		public Pizza build() {
			return new Pizza(this);
		}

	}

	private Pizza(Builder builder) {
		tamanho = builder.tamanho;
		queijo = builder.queijo;
		tomate = builder.tomate;
		bacon = builder.bacon;
	}

}

Aplicando o padrão de projeto Builder, temos agora um objeto “construtor” para o objeto Pizza. A classe Pizza está um pouco mais complexa, mas confira como ficou elegante a forma de temperarmos:

Pizza pizza = new Pizza.Builder(10)
                       .queijo()
                       .tomate()
                       .bacon()
                       .build();

É muito mais fácil de codificar com essa API e entender o que está acontecendo.

O Builder Pattern é muito utilizado em boas bibliotecas que disponibilizam APIs intuitivas e fáceis de aprender, como construtores de XML e o Response do JAX-RS, por exemplo.

Composição de Objetos

Outro ponto onde temos dificuldades encontra-se na criação de objetos que são compostos por vários objetos. Como exemplo, vamos montar um pedido com alguns itens para um cliente:

public class Pedido {

	List<Item> itens;
	Cliente cliente;

	void adicionarItem(Item item) {
		itens.add(item);
	}

	void setCliente(Cliente cliente) {
		this.cliente = cliente;
	}

	void fechar() {
		// ...
	}

}

O uso fica assim:

Pedido p = new Pedido();
p.setCliente(new Cliente("José"));
p.adicionarItem(new Item("Motocicleta", 1));
p.adicionarItem(new Item("Capacete", 2));
p.fechar();

Embora, neste exemplo, nosso código esteja relativamente simples, um objeto mais complexo dificultaria ao desenvolvedor entender o código. Mas, estou certo, podemos fazer muito melhor que isso!

Interface Fluente

A “mágica” das Interfaces Fluentes está em encadear métodos que retornam a instância do próprio objeto, evitando repetições desnecessárias no código “cliente”. Abaixo, o exemplo anterior foi ligeiramente modificado:

public class Pedido {

	List<Item> itens;
	Cliente cliente;

	Pedido com(int quantidade, String nome) {
		itens.add(new Item(nome, quantidade));
		return this;
	}

	Pedido para(String nome) {
		cliente = new Cliente(nome);
		return this;
	}

	void fechar() {
		// ...
	}

}

O uso fica assim:

new Pedido()
	.para("José")
	.com(1, "Motocicleta")
	.com(2, "Capacete")
	.fechar();

Muito mais limpo, com maior clareza e intuitivo, não é mesmo?

Classes Utilitárias

Temos ainda classes utilitárias com seus métodos estáticos, usadas nos mais diversos pontos da arquitetura de um sistema. Elas causam problemas quando mal planejadas, pois a tendência é acumularmos muitos métodos sobrecarregados com diferentes objetivos que infectam todo o código. Então, sem perceber, perdemos o controle, já que agora todo o sistema está acoplado a tais classes e alterações acabam impactando onde não esperamos.

Vejamos um exemplo de uma típica classe com rotinas de tratamento de datas:

public class Data {

	public static Date converteTextoParaData(String dataStr) {
		try {
			return new SimpleDateFormat("dd/MM/yyyy").parse(dataStr);
		} catch (ParseException e) {
			return null;
		}
	}

	public static String converteDataParaTexto(Date data) {
		return new SimpleDateFormat("dd/MM/yyyy").format(data);
	}

	public static Date avancarDiasCorridos(Date dataInicial, int dias) {
		Calendar c = Calendar.getInstance();
		c.setTime(dataInicial);
		c.add(Calendar.DATE, dias);
		return c.getTime();
	}

}

Eis como ficaria uma manipulação simples de data usando essa classe:

String inputDateStr = "28/02/2013";
Date inputDate = Data.converteTextoParaData(inputDateStr);
Date resultDate = Data.avancarDiasCorridos(inputDate, 30);
String resultDateStr = Data.converteDataParaTexto(resultDate);

Entediante. Vamos refatorar a classe com o conceito de Interface Fluente:

public class Data {

	private Date data;
	public Data(String dataStr) {
		try {
			data = new SimpleDateFormat("dd/MM/yyyy").parse(dataStr);
		} catch (ParseException e) {
			throw new IllegalArgumentException(e);
		}
	}

	public String toString() {
		return new SimpleDateFormat("dd/MM/yyyy").format(data);
	}

	public Data avancarDiasCorridos(int dias) {
		Calendar c = Calendar.getInstance();
		c.setTime(data);
		c.add(Calendar.DATE, dias);
		data = c.getTime();
		return this;
	}

}

E o uso fica assim:

String resultDateStr = new Data("28/02/2013").avancarDiasCorridos(30).toString();

Simples, não?

Nota: considere este como um exemplo de estudo. O uso do new para instanciar objetos é discutível, assim como o uso da classe java.util.Date.

Conclusões

Simplificar o código não é luxo. Trata-se de uma necessidade na luta contra a crescente complexidade dos sistemas de software.

A utilização de padrões de projetos como o Builder Pattern e técnicas como a Fluent Language nos auxiliam nessa empreitada, além de tornar a codificação mais simples e agradável.

Outro ponto positivo é que, ao utilizar objetos específicos para tratar problemas comuns, como no exemplo de Fluent Language, fica mais fácil manter o foco da responsabilidade das classes. Com métodos estáticos, a tendência é acrescentar novos métodos indiscriminadamente, enquanto com um objeto “inteligente” temos uma melhor percepção dos limites.

Leitura Obrigatória

Para ler mais sobre Fluent Interfaces, acesse http://martinfowler.com/bliki/FluentInterface.html.

Além disso, todo desenvolvedor deve ler o livro Design Patterns: Elements of Reusable Object-Oriented Software. Infelizmente, o livro é um tanto antigo e os exemplos podem ser um pouco “chatos” para desenvolvedores web, pois muitos aplicam-se à construção de GUI de aplicativos desktop.

Especificamente para quem está iniciando em Java, uma leitura mais leve é o Head First Design Patterns. É mais fácil de entender, porém incompleto. Não espere encontrar todos os padrões em detalhes. Muitos deles só constam no final como uma espécie de resumo.

Creative Commons O blog State of the Art de Luiz Ricardo é licenciado sob uma Licença Creative Commons. Copie, compartihe e modifique, apenas cite a fonte.