Singleton versus Static

Artigo que discute o padrão de desenvolvimento (design pattern) singleton e mostra as diferenças em relação à implementação de classes contendo exclusivamente atributos e métodos estáticos.

Introdução

Muita gente conhece o padrão Singleton. O conceito é muito simples: é um design utilizado para garantir que uma determinada classe possua uma única instância em toda a aplicação. Para isso, uma única instância é criada pela classe e sempre a mesma instância é retornada para uso. Porém, em algumas situações, é possível que uma classe agrupe métodos e atributos estáticos, como se uma única "instância" fosse manipulada. Neste post vou analisar as diferenças sobre o padrão Singleton e uma classe com métodos e atributos estáticos.

Contextualização

Como já foi citado anteriormente, o padrão singleton é utilizado para que uma única instância de uma determinada classe seja manipulada em qualquer lugar da aplicação em que for solicitada. A implementação disso é quase sempre igual, e você pode até ver um exemplo em um artigo antigo deste blog, que falava sobre a utilização do self. Um exemplo prático de utilização do padrão singleton em um ambiente web é uma classe que recupera configurações do sistema. Embora não seja algo obrigatório, é útil que as configurações sejam lidas de um lugar uma única vez e depois seus valores possam ser recuperados em qualquer lugar do sistema.

Porém, este mesmo comportamento pode ser feito com uma classe que possui apenas atributos e métodos estáticos. Ou seja, não existe uma instância da classe, mas os dados podem ser armazenados estaticamente e manipulados pelos métodos estáticos.

Vantagens e Desvantagens

Diante da contextualização, você pode se perguntar: quando devo usar o padrão singleton e quando devo implementar uma classe com métodos/atributos estáticos?

Então vejamos algumas coisas que você consegue com o padrão singleton e que não consegue com uma classe com métodos/atributos estáticos:

  • Um construtor que realiza operações assim que a instância singleton é criada pela primeira (e única) vez.
  • Passar um objeto singleton como parâmetro para um método.
  • Desalocar o objeto singleton da memória.

Vejamos: uma classe com métodos/atributos estáticos até pode simular um construtor, mas teria que checar se ele foi executado a cada vez que um método estático é chamado, perdendo em performance. Não é possível passar uma classe que só tem métodos/atributos estáticos como parâmetro para algum método, afinal, não existe uma instância real da classe. E, finalmente, não é possível desalocar uma classe com métodos/atributos estáticos pois não existe instância. No máximo, você conseguiria desalocar os valores dos atributos estáticos, mas certamente causaria alguns inconvenientes para recuperá-los depois.

O melhor do padrão singleton, é que, além de poder fazer tudo que foi citado, a instância singleton também pode fazer o que uma classe com métodos/atributos estáticos faz. Ou seja, na dúvida, é melhor ter uma implementação singleton.

Na minha opinião, métodos e atributos estáticos só devem mesmo ser criados quando realizam operações para uma classe que não precisam de uma instância. Pode, por exemplo, implementar algum cálculo especial, utilizado ou não pela classe, mas que está relacionado com o propósito da mesma. Portanto, antes de fazer uma classe que só tem métodos e atributos estáticos, pense realmente se você não está tentando simular o padrão singleton e lembre-se do que você poderá perder com a modelagem adotada.

7 comentários

Anônimo disse...

Em PHP, o comportamento do design pattern Singleton não é o esperado pois a cada refresh uma nova classe é instanciada.

Rubens Takiguti Ribeiro (autor do blog) disse...

Olá, Anônimo.

Na verdade o cenário Web, baseado em transações entre cliente/servidor, é que tem uma grande diferença contextual em relação a alguns padrões.

Eu poderia, por exemplo, considerar que a execução de um script é a execução de um programa e, usando singleton, eu teria o mesmo objeto durante a execução do programa. Um refresh significaria encerrar a execução do programa e iniciar outro.

Por outro lado, há alternativas para armazenar objetos entre uma transação e outra, de forma serializada. É possível, inclusive, manter um serviço que tem objetos instanciados e recebe requisições para realizar operações sobre estes objetos.

Ou seja, você não pode afirmar que, em PHP, o comportamento do design pattern Singleton não é o esperado, pois o design só define um comportamento e a linguagem dá recursos para se obter este comportamento. O que pode ocorrer é a implementação "tradicional" não se aplicar a um tipo de ambiente.

Anônimo disse...

Muito bom mesmo, me ajudou muito a esclarecer melhor esta diferença.

Paulo disse...

Apesar de Antigo o Post eu gostei bastante. Achei interessante a forma que você explicou. Mas gostaria de fazer uma pergunta.
Eu comecei a desenvolver a pouco tempo e queria entender melhor. Se o caso de utilizar o mesmo objeto, com os mesmos valores. Eu não poderia armazenar esse objeto numa Sessão? Utilizo o padrão Singleton para acesso ao banco, e utilizo Static para abstração das tabelas do banco. Não sei se é o melhor cenário, mas consigo recuperar sempre os valores e com uma boa performance.

Rubens Takiguti Ribeiro (autor do blog) disse...

Paulo, embora seja possível armazenar objetos em sessão, eu não acho que seja adequado nesse contexto. A sessão deveria ser usada para se guardar informações úteis sobre a navegação de um usuário. A informação que mais se aparece em sessão é o "id" do usuário que se autenticou, para garantir que o site não precise pedir as credenciais de acesso para cada nova página acessada (depois que o usuário já se logou a primeira vez, é claro).

Se você precisa de um objeto que a cada requisião terá os mesmos valores, não há problema em você recriá-lo em cada requisição. No PHP para web, por se tratar de um ambiente transacional de requisições/respostas, é assim que é mais comum. Não se costuma fazer um singleton real, de fato, em que o mesmo objeto fica na memória. O que existe é o singleton por requisição.

Quanto ao static para abstração de tabelas do banco, não sei se entendi direito, mas parece que para cada tabela você tem uma classe com métodos estáticos, é isso? Se for o caso, eu acho que faz mais sentido se utilizar um objeto único por tabela (sem static). Assim você pode optar por descartá-lo em determinado momento do código, poupando memória.