Padrões de Projeto Padrões de Criação Singleton Singleton Assegura que uma classe tenha apenas uma instância e provê um ponto de acesso global a ela 2 Livro Texto: Design Pattern - Elements 1
Motivação É importante a algumas classes ter exatamente uma instância Ex.: várias impressoras, mas apenas um spool de impressão. Um único sistema de arquivos, um único gerenciador de janelas etc Como garantir a única instância e o acesso fácil a ela? Variável global? é possível instanciar múltiplos objetos... 3 Livro Texto: Design Pattern - Elements Motivação A melhor solução é fazer a própria classe responsável por manter rastreável sua única instância. Assegurar que nenhuma outra instância seja criada (interceptando requisições de criação de novos objetos) Prover uma forma de acesso a esta instância Esta solução é o padrão Singleton 4 Livro Texto: Design Pattern - Elements 2
Aplicação Use Singleton quando: Deve haver exatamente uma instância de uma classe e ela deve ser acessível a clientes a partir de um ponto de acesso bem conhecido A instância única deve ser extensível via especialização e os clientes devem ser aptos a usar uma instância estendida sem modificar seu código 5 Livro Texto: Design Pattern - Elements Estrutura 6 Livro Texto: Design Pattern - Elements 3
Participante Singleton Define uma operação Instance que permite aos clientes acessarem sua instância única. Instance é uma operação da classe (isto é, uma função membro estática em C++) Pode ser responsável por criar sua própria instância única 7 Livro Texto: Design Pattern - Elements Colaborações Clientes acessam a instância de um Singleton apenas através da operação Instance do Singleton 8 Livro Texto: Design Pattern - Elements 4
Conseqüências 1. Acesso controlado à instância única 2. Espaço de nomes reduzido 3. Permite um número variável de instâncias 4. Mais flexível que operações de classe 9 Livro Texto: Design Pattern - Elements Assegurar uma instância única: esconder a operação que cria a instância atrás de uma operação de classe que garanta que somente uma instância seja criada 10 Livro Texto: Design Pattern - Elements 5
Note que o construtor é protegido. Um cliente que tente inicializar Singleton diretamente receberá um erro em tempo de compilação Note também que Instance usa lazy initialization 11 Livro Texto: Design Pattern - Elements Veja que não basta apenas definir o singleton como uma variável global ou objeto estático, porque: Não é possível garantir que somente uma instância de um objeto estático seja declarada Pode-se não ter toda a informação necessária para a instanciação de todo singleton no instante da inicialização estática (C++ não define a ordem na qual os construtores para objetos globais são chamados, isto significa que não pode haver nenhuma dependência entre singletons, caso contrário, erros serão inevitáveis) Além, é claro, da desvantagem de objetos globais/estáticos (todos os singletons seriam criados, independente deles serem usados ou não) 12 Livro Texto: Design Pattern - Elements 6
Exemplo: Objeto único para conexão com Banco de Dados Modelo UML Singleton Código java public class FileLogger implements Logger { private static FileLogger logger; Construtor é privativo e não pode ser acionado de fora da classe //Prevent clients from using the constructor private FileLogger() { } public static FileLogger getfilelogger() { if (logger == null) { logger = new FileLogger(); } return logger; } public synchronized void log(string msg) { FileUtil futil = new FileUtil(); futil.writetofile("log.txt",msg, true, true); } } FileLooger, que é método de classe (static) e não de instância: a) cria a instância, aciona o construtor (se logger não existe) b) Retorna a instância criada, se objeto logger já existir log() deve ser syncronized pois sendo uma única instância devese evitar race conditions, uma vez que este objeto será o único a tratar as requisições que podem partir de vários clientes 7
Especializando classes Singleton A técnica mais simples é determinar qual singleton se quer usar de dentro da operação Instance (p.ex., via variável de ambiente) Outra forma de se escolher a subclasse de Singleton é colocar Instance dentro das subclasses (linka-se um objeto que contém uma implementação diferente, portanto, escondendo-se isto da implementação do cliente) Uma terceira forma é usar um registro de singletons 15 Livro Texto: Design Pattern - Elements Exemplo do Labirinto No exemplo da MazeFactory, precisa-se apenas de uma única instância da fábrica de labirinto. Exemplo 1: considera-se que nunca haverá subclasses de MazeFactory Exemplo 2: há subclasses de MazeFactory 16 Livro Texto: Design Pattern - Elements 8
Exemplo 1 17 Livro Texto: Design Pattern - Elements Exemplo 1 18 Livro Texto: Design Pattern - Elements 9
Exemplo 2 19 Livro Texto: Design Pattern - Elements Exemplo 2 Note que Instance deve ser modificada sempre que se define uma nova subclasse de MazeFactory Uma possível solução é o registro de singletons Outra solução é fazer linkagem dinâmica. 20 Livro Texto: Design Pattern - Elements 10
- Registro Ao invés de Instance definir um conjunto de possíveis classes Singleton, as classes Singleton registram suas instâncias pelo nome em um registro bem conhecido O registro mapeia entre nomes e singletons Quando Instance precisa de um singleton, ele consulta o registro pelo nome O registro procura pelo singleton correspondente (se existe) e o retorna Esta técnica libera Instance de ter que conhecer todas as possíveis classes de Singleton 21 Livro Texto: Design Pattern - Elements 22 Livro Texto: Design Pattern - Elements 11
23 Livro Texto: Design Pattern - Elements Onde as classes Singleton se registram? Em seu construtor? Problema: o construtor só é chamado se a classe já tiver sido chamada... 24 Livro Texto: Design Pattern - Elements 12
Solução: definir uma instância estática Neste caso, a classe não é mais responsável por criar o singleton, mas apenas torná-lo acessível no sistema. A desvantagem é que as instâncias de todas as possíveis subclasses Singleton devem ser criadas ou não serão registradas 25 Livro Texto: Design Pattern - Elements 13