Hoje vamos falar pouco sobre o padrão Singleton. Padrão de projeto que coitado, é pouco solitário. É o Singleton. E qual que é a ideia so Singleton? A gente usa o Singleton primeiro quando a gente, é jeito de criar novos objetos. Então, padrão de criação de objetos então ele fala sobre a instanciação, a criação de novos objetos. Só que ele tem uma coisa muito particular. A gente vai usar ele quando a gente quer uma classe que tenha uma única instancia daquela classe, normalmente uma classe você pode criar quantas instancias quiser, quantos objetos quiserem, você quiser daquela classe. Mas alguns casos a gente quer criar uma única instancia. Daí o padrão single tem jeito de garantir que a classe tenha uma única instancia. Além disso esse padrão provê meio, uma forma de você ter acesso àquela instância de qualquer pondo do seu programa. De uma forma global de qualquer lugar você consegue facilmente acessar àquela única instancia. Por que eu poderia querer usar uma Singleton. Você vai usar Singleton Quando você tem uma aplicação onde faz sentido ter uma única instancia de uma determinada classe. Então por exemplo se é jogo de xadrez jogo de xadrez só faz sentido ter único tabuleiro de xadrez. Onde você vai ter as peças. Não faz sentido ter monte de tabuleiro de xadrez num jogo de xadrez. Então, a classe que representa o tabuleiro de xadrez faria sentido ser Singleton, porque eu teria uma única instancia daquele tabuleiro de xadrez. Ou se eu tenho jogo de vídeo game espacial alí que é de brincar de pilotar a nave espacial, e eu tenho uma classe para representar o espaço sideral. Onde dentro dessas classes contém informações sobre as coisas que tem no jogo. É único espaço é único universo então, faria sentido ter uma única classe chamada espaço que guardaria dentro de si os objetos que estão naquele jogo. Seria único, faz sentido ser singleton. Às vezes a gente tem software que vai guardar coisas no banco de dados e é único banco de dados. Então talvez a conexão ao banco de dados, faz sentido ter singleton como uma conexão ao banco de dados. Porque vai ser único banco de dados e de qualquer ponto do meu programa facilmente eu tenho acesso aquele único banco de dados. Então, poderia ser singleton. Ou se eu estou implementando sistema operacional e esse sistema operacional vai ter gerenciador de janelas onde vai aparecer todas as janelinhas alí do meu sistema. É único gerenciador de janelas. É uma única tela alí que eu tenho no meu sistema operacional. Então, faria sentido ter único gerenciador de janelas. Então, tem vários casos aí você vai ver se na sua aplicação faz sentido ou não ter Singleton. Mas vamos ver como a gente implementa Singleton. Então como implementar? Java, primeiro eu tenho que garantir que tenha uma única instância. Como que eu posso fazer isso? Tem uma técnica. É eu deixar o construtor privado porque se eu deixar o construtor privado só aquela mesma classe vai poder instanciar. Subclasses ou outras classes não vão poder instanciar esses objetos. Outra dica é uma vez que eu crio essa única instancia é eu guardar essa instância, uma referência para essa instância como uma variável static, uma variável da classe. Assim eu consigo acessar ela facilmente. E como que eu acesso? Tem que oferecer método de acesso. Então, crio nessa classe método de acesso aquela instância única. Mas vamos ver então como que a gente poderia implementar o singleton. Tá, não, espera esse não é singleton. Esse é outro singleton, não. Esse aqui é singleton que não faz bem para a saúde. Singleton que faz bem para a saúde é este aqui, é o Eclipse e eu vou aqui criar uma classe. Vamos, vou usar aquele exemplo do tabuleiro de xadrez que eu gosto. Então crio aqui novo projeto, eu vou chamar de Chess. Projeto chess de xadrez. E nesse projeto eu vou criar uma classe ChessBoard. O tabuleiro de xadrez, ChessBoard. Não é? ChessBoard. Termina aqui. Então, criei a minha classe ChessBoard. Então, primeira coisa a gente quer ter jeito de guardar a instância que a gente está criando. Então eu vou criar uma variável privada aqui chamada de tipo ChessBoard, que vai guardar algo dentro do tipo chessboard e eu vou chamar de instance. Vai ser a minha instância. E eu vou fazer, tem dois jeitos de fazer o singleton eu vou no primeiro jeito, assim que eu criar classe eu já crio a minha única instância. Então eu faço new ChessBoard aqui. Então pronto, já tem a minha instância única. Agora eu preciso de uma forma de acessar aquela instância única, não é? Para isso eu vou criar método chamado getInstance, que o que esse método vai fazer, ele simplesmente vai devolver essa minha instância única. Que se chama instance. Pronto, dessa forma já fiz metade do padrão singleton, porque já crio uma instância única e tenho uma forma de acessar essa instância única. Mas por enquanto qualquer outra classe, qualquer código outra classe vai poder criar novas instâncias do ChessBoard. Eu quero proibir isso? Como que eu proíbo? Eu venho aqui e faço, digo que o construtor é privado. Então falo private, Chessboard e defino construtor. Eu vou fazer construtor vazio, abro e fecha chaves. Pronto, ao fazer com que o ChessBoard construtor é privado, nenhuma outra classe, nem as minhas sublasses vão conseguir criar novas instâncias do ChessBoard. Então assim, eu já tenho aqui objeto do tipo singleton, ChessBoard que é singleton. Vamos experimentar fazer método main aqui só pra gente ver como que a coisa funcionaria. Eu faria uma public static void main (string args), já aqui eu faria ChessBoard tabuleiro recebe ChessBoard.get instance. Esse aqui seria o jeito de eu, instance, obter aquele objeto. Opa está dando uma mensagem de erro aqui, qual que é a mensagem de erro? Não posso fazer uma referência estática para método que não é estático. Na verdade então, eu quero chamar esse método get instance mesmo sem ter o objeto. Então, esse vai ser tem que ser método da classe, para eu poder chamar ele assim com o nome da classe objeto. Então, para ser o método da classe eu tenho que botar que ele é static. Fazendo static. Daí, o que que eu fiz aqui de errado? Ele tem que, essa variável tem que ser static também. Tem que ser uma variável da classe, não pode ser uma variável de instância. Agora deu tudo certo. Então, dessa forma eu consigo criar aqui tabuleiro e pego ele aqui, a instância por meio desse get instance. Essa é uma primeira forma muito simples do padrão singleton mas não é a forma mais interessante. Quando que este objeto é criado? Ele é criado quando a classe é carregado pela primeira vez no sistema então quando eu rodo meu programa java a JVM a máquina virtual vai carregar essa classe ChessBoard e quando ela carrega essa classe no processo de carregar a classe na memória ela já instancia aqui novo ChessBoard. Muitas vezes não é isso que eu quero, eu não quero que quando o sistema é iniciado monte de objetos sejam criados. Às vezes a gente gosta de fazer uma coisa que chama instanciação tardia. Inglês isso seria Lazy Instantiation. O que é isso? Eu só crio o Chessboard, eu só crio o objeto quando eu realmente preciso do objeto. E esse é jeito bem, eu diria mais elegante e pode trazer ganhos de desempenho porque ele não fica criando objetos a toa quando os objetos não são criados. Ele só cria objetos no momento que o objeto é utilizado. Então, como eu vou fazer isso? Vez de eu já inicializar aqui eu vou inicializar com o nulo aqui. Então, inicialmente a instância fica sendo nula. E daí eu vou mudar aqui a implementação do get instance. Como que eu vou fazer a implementação do get instance? Eu vou fazer o seguinte. Quando ele pede a instância do meu objeto, eu faço o seguinte: primeiro eu verifico se aquela instância, se aquele objeto foi instanciado ou não, se aquela classe já gerou o seu objeto singleton. Como que eu faço isso? Eu faço if instance. Se for igual a null, preciso dos parênteses. Se for igual a null significa que eu ainda não criei. Então, eu preciso criar nesse caso a minha instância. Então, eu faço instance, recebe aqui agora nesse momento eu instancio, ChessBoard. E eu devolvo a minha instância aqui. Caso contrário, o caso contrário é o caso que "instance" não é "null", ou seja, a instância já tinha sido criada numa chamada anterior ao "get instance", daí eu devolvo a instância. Na verdade tem uma refaturação que dá para fazer nesse código porque ambos os casos, tanto "if" como "else" faz o "return instance" então eu posso tirar isso para fora do "if", eu apago isso aqui e tiro o "else" porque sempre é devolvido, então faço isso. E daí eu não preciso das chaves, ficou o código bem mais enxuto. Então, se a instância é nula, eu instancio o "ChessBoard" e todos os casos eu devolvo a nova instância. Então agora isso que a gente chama de instanciação tardia, vamos ver aqui exemplo dele funcionando. Vamos supor que eu quero criar dois tabuleiros. Na verdade só estou testando isso. Vou criar dois tabuleiros aqui, então vou copiar essa linha. Copiar linha. Esse aqui vai ser o tabuleiro 1 e o tabuleiro 2 e depois eu vou mandar imprimir. "System.out.println" tabuleiro 1. E agora vou copiar isso aqui e fazer igual porque eu quero imprimir os dois tabuleiros. Tabuleiro 2. Eu vou mandar executar e ver o que acontece. eu falei que o Singleton permite a criação de uma única instância, único objeto. Então mesmo eu chamando "get instance" duas vezes e colocando variáveis tabuleiro tabuleiro dois, deveria ser o mesmo. E é isso que a gente está vendo aqui baixo, porque quando eu mandei imprimir ele falou que nos dois casos imprimiu a mesma coisa. Falou que é objeto do tipo "chessboard" e ele está na posição de memória que tem esse número D716361 é a posição de memória onde aquele objeto está guardado. É a mesma posição de memória, então é o mesmo objeto. Eu posso chamar "get instance" quantas vezes eu quiser e ele vai devolver sempre o mesmo objeto. Esse é o padrão Singleton. Singleton tem desvantagens. Primeiro, uso excessivo de Singleton não é algo razoável, se você tem programa e tem 5 ou 10 Singleton tem algo de estranho no seu programa. Você está usando uma classe quase como se fosse uma variável. Na verdade, o Singleton ele se parece como uma variável global porque de qualquer ponto do seu programa, você escreve não é da classe .get instance, você tem acesso aquele objeto. isso é como se fosse uma variável global que existe na linguagem C por exemplo e a gente vê que variável global não é uma boa coisa porque é difícil de entender o código da manutenção por isso que Java proíbe a existência de variáveis globais, o Singleton tem jeito de hackear a linguagem para criar o conceito de variável Global. Você pode até usar mas com muito cuidado, com muita parcimônia. Sem excessos. Então resumindo, a gente usa o Singleton quando a gente quer uma única Instância de uma determinada classe e quando a gente quer ter tem jeito fácil de acessar essa instância. Essa instanciação pode ser quando a classe é carregada ou o jeito mais sofisticado do Singleton que é essa instanciação tardia, que inglês se chama de Lazy Instantiation. Antes de terminar eu quero mostrar uma outra implementação do Singleton que é feita por colega meu que é o Joe Yoder, que é grande especialista padrões de projetos orientados a objetos, é grande especialista desenvolvimento de software, o Joe Yoder, e ele uma vez mostrou uma implementação que eu gostei muito. Essa aqui. O que é que ele fez? Ele fez uma classe abstrata cujo nome é Singleton. E daí, se eu tivesse a classe abstrata no meu programa, se eu quiser criar uma outra classe que seja Singleton basta eu fazer essa minha classe ser uma Subclasse dessa classe Singleton. Então basta eu criar o meu chessboard, o meu tabuleiro de xadrez, fazendo ele ser uma subclasse do Singleton, automaticamente toda a funcionalidade Singleton já está pronto. Daí, na minha classe de tabuleiro de xadrez eu posso colocar só os métodos relacionados a tabuleiro de xadrez, não precisa colocar nada de Singleton. Então como é que funciona essa classe abstrata? Ele cria o construtor Protected. Protected é para permitir que as minhas subclasses façam a instanciação, então o tabuleiro de xadrez vai poder fazer. E ele define esse método "makeInstance", como método abstrato também, eu vou ter que implementar isso aqui na minha subclasse. Na minha Subclasse eu vou instanciar o tabuleiro de xadrez. E daí tem método privado que vai guardar a instância única do Singleton e tem esse método "instance", que é método que faz essa lógica aqui. Se a Instância única é "null", quer dizer que não foi criada ainda, eu chamo esse "makeInstance" na minha Subclasse que no meu caso do meu exemplo vai ser para criar o tabuleiro de xadrez. E ambos os casos eu devolvo essa soleInstance. Então qualquer usuário da minha classe tabuleiro de xadrez ou qualquer outro Singleton que eu gere simplesmente vai colocar o nome da classe .instance e vai receber as instâncias. E toda essa lógica do Singleton fica nessa classe abstrata que vai ser a minha superclasse. É jeito mais sofisticado, a vantagem é que você escreve uma única vez essa lógica do Singleton, coloca nessa classe abstrata e depois você pode criar vários Singleton simplesmente usando herança vez de reimplementar a lógica do Singleton. Fica ao seu critério o que é que você vai fazer. Por hoje é só, espero que vocês tenham gostado do padrão Singleton e até a próxima.