CodeGym /Blogue Java /Random-PT /Padrões e Singleton em Java
John Squirrels
Nível 41
San Francisco

Padrões e Singleton em Java

Publicado no grupo Random-PT
Este artigo destina-se a qualquer um que, pela primeira vez, esteja encontrando o conceito de padrões de projeto, tenha ouvido o termo singleton ou de alguma forma implementado o padrão singleton, mas não entendeu o que estava acontecendo. Bem-vindo! Os alunos do CodeGym encontram padrões de design pela primeira vez no nível 15, quando o capitão inesperadamente pede que eles "reforcem" sua compreensão implementando o padrão Java Singleton com implementação preguiçosa. Os alunos que ouvem sobre o padrão singleton pela primeira vez instantaneamente têm muitas perguntas: o que diabos é um padrão de projeto? Por que precisamos disso? O que é um singleton ? E, finalmente, o que é implementação preguiçosa? Vamos responder a essas perguntas em ordem.

O que no mundo é um padrão de design?

Eu acredito que um pouco de história é para responder a esta pergunta com a melhor compreensão. Existem quatro autores de programação famosos (Erich Gamma, John Vlissides, Ralph Johnson e Richard Helm) que tiveram uma ideia interessante. Eles perceberam que o desenvolvimento de software geralmente exigia que eles resolvessem aproximadamente os mesmos problemas e escrevessem códigos estruturados da mesma maneira. Então eles decidiram descrever padrões típicos que muitas vezes precisam ser usados ​​na programação orientada a objetos. Seu livro foi publicado em 1994 sob o título Design Patterns: Elements of Reusable Object-Oriented Software. O nome do livro acabou ficando muito longo e as pessoas começaram a chamá-lo simplesmente de livro da Gangue dos Quatro. A primeira edição incluiu 23 padrões. Posteriormente, dezenas de outros padrões foram descobertos.
Um padrão de projeto é uma solução padronizada para um problema comum.
E o padrão singleton é apenas um deles.

Por que precisamos de padrões de projeto?

Você pode programar sem conhecer os padrões: afinal, no nível 15, você já escreveu centenas de miniprogramas no CodeGym sem saber que eles existem. Isso sugere que os padrões de projeto são um tipo de ferramenta cujo uso distingue o mestre do amador: os padrões de projeto descrevem como resolver adequadamente um problema típico. Isso significa que conhecer os padrões economiza seu tempo. Dessa forma, eles são semelhantes aos algoritmos. Por exemplo, você pode criar seu próprio algoritmo de classificação com blackjack e númerose gastar muito tempo fazendo isso, ou você pode implementar um que tenha sido compreendido e descrito por muito tempo. O mesmo acontece com os padrões de projeto. Além disso, com os padrões de projeto, o código se torna mais padronizado e, ao usar o padrão apropriado, é menos provável que você cometa erros, pois as armadilhas comuns do padrão foram identificadas e eliminadas há muito tempo. Acima de tudo, o conhecimento dos padrões ajuda os programadores a se entenderem melhor. Você pode simplesmente dizer o nome de um padrão em vez de tentar fornecer uma longa explicação para seus colegas programadores. Resumindo, os padrões de projeto ajudam você a:
  • não reinventar a roda, mas sim usar soluções padronizadas;
  • padronizar código;
  • padronizar a terminologia;
Para concluir esta seção, notamos que todo o corpo de padrões de projeto pode ser dividido em três grandes grupos: Padrões e singleton - para todos os que os encontram pela primeira vez - 2

Finalmente, o padrão singleton

Singleton é um padrão criacional . Esse padrão garante que haja apenas uma instância de uma classe e fornece um ponto de acesso global para esse objeto. Pela descrição, deve ficar claro que esse padrão deve ser aplicado em dois casos:
  1. quando seu programa requer que não mais do que um objeto de uma classe particular seja criado. Por exemplo, um jogo de computador pode ter uma classe Hero e apenas um objeto Hero que descreva o único herói do jogo.

  2. quando você precisa fornecer um ponto para acesso global a um objeto. Em outras palavras, você precisa disponibilizar o objeto em qualquer lugar do programa. Infelizmente, não basta simplesmente criar uma variável global, pois ela não é protegida contra gravação: qualquer pessoa pode alterar o valor da variável, portanto, o ponto de acesso global do objeto pode ser perdido. Essas propriedades de um Singleton são necessárias, por exemplo, quando você tem um objeto que trabalha com um banco de dados e precisa acessar o banco de dados de diferentes partes do programa. Um Singleton garantirá que ninguém escreva código que substitua a instância criada anteriormente.
Portanto, um Singleton satisfez essas duas necessidades: deve haver apenas um determinado tipo de objeto no programa e deve haver acesso global a ele. No exemplo do nível 15, o capitão pede que você implemente esse padrão para a seguinte tarefa:
  1. Encontre um exemplo de Singleton com inicialização lenta.

  2. Crie três classes singleton — Sun, Moon, Earth — em arquivos separados usando o mesmo princípio.

  3. ImplementoPlanetainterface nas classes Sol , Lua e Terra .

  4. No bloco estático da classe Solution chame oreadKeyFromConsoleAndInitPlanetmétodo.

  5. Implemente oreadKeyFromConsoleAndInitPlanetFuncionalidade do método:

    • 5.1. Leia um parâmetro String do console

    • 5.2. Se o parâmetro for igual a um dosPlanetaconstantes da interface, crie um objeto thePlanet adequado .

Depois de ler atentamente as condições da tarefa, podemos ver claramente por que um Singleton é necessário aqui. De fato, somos solicitados a criar uma instância de cada uma das seguintes classes: Sun , Moon , Earth . Faz sentido assumir que não devemos criar mais do que um Sol/Lua/Terra. Caso contrário, entraremos em uma situação absurda, a menos, é claro, que você esteja escrevendo sua versão de Star Wars. Implementando o padrão Singleton em Java em três etapas Em Java, o comportamento Singleton não pode ser implementado usando um construtor comum, porque um construtor sempre retorna um novo objeto. Portanto, todas as implementações de Singletonresume-se a ocultar o construtor, criando um método estático público que controla o tempo de vida do objeto singleton e "destruindo" todos os novos objetos que aparecem. Se um Singleton for acessado, ele deve criar um novo objeto (se ainda não existir um no programa) ou retornar um existente. Para realizar isso:
  1. Você precisa dar à classe um campo estático privado que armazene um único objeto:

    
    public class LazyInitializedSingleton {
    	private static LazyInitializedSingleton instance; // #1
    }
    
  2. Torne o construtor (padrão) privado. Isso significa que ela não pode ser acessada fora da classe e não poderá retornar novos objetos:

    
    public class LazyInitializedSingleton {
    	private static LazyInitializedSingleton instance;
    private LazyInitializedSingleton(){} // #2
    } 
    
  3. Declare um método de criação estático que será usado para obter o singleton:

    
    public class LazyInitializedSingleton {
        private static LazyInitializedSingleton instance;
            private LazyInitializedSingleton() {}
            public static LazyInitializedSingleton getInstance() { // #3
            if (instance == null) { // If the object has not yet been created
                instance = new LazyInitializedSingleton(); // Create a new object
            }
            return instance; // Return the previously created object
        }
    }
    
O exemplo acima é um tanto desajeitado, pois simplesmente ocultamos o construtor e fornecemos nosso próprio método em vez de um construtor padrão. Como este artigo visa garantir que os alunos do CodeGym entrem em contato com esse padrão (e com os padrões de projeto em geral), as nuances de implementações singleton mais complexas não serão descritas aqui. Observamos apenas que, dependendo da complexidade do programa, esse padrão pode precisar ser mais refinado. Por exemplo, em um ambiente multithread (veja artigos sobre threads), vários threads diferentes podem acessar o método singleton simultaneamente, e o código descrito acima deixará de funcionar, pois cada thread separado pode criar uma instância da classe. Como resultado, ainda existem várias abordagens diferentes para criar singletons thread-safe adequados. Mas isso é outra história =)

E finalmente... O que é essa inicialização preguiçosa que o capitão perguntou?

A inicialização preguiçosa também é chamada de inicialização adiada. Este é um truque de programação em que uma operação de uso intensivo de recursos (e a criação de um objeto é uma operação de uso intensivo de recursos) é executada sob demanda, e não antecipadamente. Então, o que realmente acontece em nosso código Java Singleton ? Em outras palavras, nosso objeto é criado no momento em que é acessado, não antecipadamente. Você não deve assumir que a inicialização preguiçosa está de alguma forma rigidamente ligada ao padrão Singleton . A inicialização preguiçosa também é usada em outros padrões de design criacional, como Proxy e Factory Method, mas isso também é outra história =)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION