0

Série Design Patterns com PHP: I - Singleton

criado por Adler Medrado em 18/07/2006 3:24pm
<u>Vendo na prática como funciona:</u>

Primeiramente, os membros e métodos da nossa classe que serão utilizados tem de ser estáticos, ou seja, podem ser chamados de fora do contexto de uma instância da classe.
Com isso em mente iremos criar uma classe de exemplo que abre uma conexão com um banco de dados mySQL.

<?php 
class Conexao {
    public static $instance;
    
    /*
     * Colocando o constructor como private impede que a classe seja instanciada.
     */
    private function __construct() {
        print 'Sou o constructor da classe '.__CLASS__.' e esta classe não deve ser instanciada.';
    }

    public static function singleton() {
        if (!isset(self::$instance) {
	    // Você deve informar os dados para conexão com o banco de dados.
            self::$instance = new mysqli($host,$user,$password,$db);
        } 
        return self::$instance;
    }
}
?>

Como vemos, nós definimos como estáticos a propriedade $instance e o método singleton(), com isso poderemos acessá-los independentemente da classe ter sido instanciada ou não.
Os nomes $instance e $singleton são convenções, não tendo nenhuma interferência na funcionalidade do pattern.

A classe acima poderia ser utilizada da seguinte forma:

<?php 
$conexao = Conexao::singleton();
$conexao->query($sql);
?>

Nós recuperamos uma instância da conexão com o banco de dados, executando a função <u>singleton</u> da classe Conexao, estaticamente, utilizando '::' e se tentássemos instanciar a classe Conexao, nós teríamos um erro gerado porque o construtor da classe foi definido como private, ou seja, só pode ser acessado de dentro da própria classe.

Nota-se que dentro da classe, nós trabalhamos com a propriedade $instance da mesma forma, por meio da palavra chave self, que indica que estamos utilizando um membro estático da classe.

Um membro estático de uma classe não pode ser chamado via $this como é comumente feito devido ao fato de ele ser um membro da classe e não um membro da instância da classe.

Caso não existisse nenhuma instância anterior do objeto mysqli ( if (!isset(self::$instance) ) uma instância seria criada e logo em seguida retornada pelo método, caso contrário, a mesma instância criada anteriormente seria retornada.

Ao ter o objeto de conexao, manipulamos ele normalmente utilizando seus proprios métodos.


Considerações finais:
A utilização do pattern singleton permite uma facilidade maior no controle das classes que não devem ser instanciadas mais do que uma vez.

Normalmente uma classe singleton é utilizada em conjunto com uma classe factory, que será o pattern que iremos ver na próxima semana.

Espero que tenha sido útil.
Caso deseje entrar em contato, pode faze-lo através do email: adler@neshertech.net

Comentários:

Mostrando 1 - 10 de 16 comentários
olá, gostaria de saber um forum bom para tirar duvidas sobre Design Patterns, alguem poderia me indicar? ja que vejo que este é mais para artigos
28/03/2017 1:12pm (~7 anos atrás)

augustowebd disse:
salve a todos,
estive estudando o padrão singleton e vendo vários exemplos de sua aplicabilidade e cheguei a uma dúvida:
"é correto dizer que usamos o padrão siglenton para conexões a db?", me pergunto pq veja bem:
- Se temos uma projeto, consistente, de uma aplicacao onde temos nossas áreas bem definidas e sabemos que na área A o usuário só irá efetua login( consulta ), na área B ele só visualizará relatórios( consulta ), sem falar de que cada ação permitida à tal usuário vai ser de acordo sua permissao, ou seja, dependendo do tipo de permissão do usuário em questão ele jamais efetuará uma alteração no banco, então o projetista do sistema acha interessante usar singleton para estabelecer uma conexao ao DB, temos no mínimo um impasse:
1º - Estabelecer varias conexões diferentes, de acordo apermissao.
2º - estabelecer uma conexao com excesso de permissao para um usuário que, de antemão, sabemos que não usará todos os recursos disponiveis pela conexao, o que em meu ponto de vista dá margem para insegurança.

Ae eu pergunto, é correto usar singleton para estabelecer conexões com banco de dados?
30/10/2006 8:18am (~17 anos atrás)

Eziel disse:
Apenas algumas sugestões e observações:
Vejamos uns exemplos com o código a seguir:

---------------------------------
<?php
class Exemplo {
public static $instance;

private function Exemplo()
{
}

public static function getInstance()
{
if (!isset(self::$instance)) {
Exemplo::$instance = new Exemplo();
}
return Exemplo::$instance;
}
}
$a = Exemplo::getInstance();
var_dump ( $a );

$a = Exemplo::getInstance();
var_dump ( $a );

$b = clone $a;
var_dump ( $b );
?>
---------------------------------
Executando temos:

---------------------------------
$ php exemplo.php
object(Exemplo)#1 (0) {
}
object(Exemplo)#1 (0) {
}
object(Exemplo)#2 (0) {
}
---------------------------------


Nota-se que obtendo a instância através do método getInstance(), realmente sempre obtemos a mesma instância da classe ( object(Exemplo)#1 ). Mas quando clonamos um objeto, utilizamos a palavra chave "clone", obtemos uma nova instância da classe ( object(Exemplo)#2 ), ou seja, serão objetos diferentes.

Em alguns casos, duas instâncias de uma mesma classe podem trazer alguns problemas, então é necessário certificar-se também que o objeto não será clonado. Podemos fazer isso da seguinte forma:

---------------------------------
class Exemplo {
public static $instance;

private function Exemplo()
{
}

public static function getInstance()
{
if (!isset(self::$instance)) {
Exemplo::$instance = new Exemplo();
}
return Exemplo::$instance;
}

public function __clone()
{
die('unable to clone... sorry');
}
}
$a = Exemplo::getInstance();
var_dump ( $a );

$a = Exemplo::getInstance();
var_dump ( $a );

$b = clone $a;
var_dump ( $b );
?>
---------------------------------
Executando temos:

---------------------------------
$ php exemplo.php
anmsxobject(Exemplo)#1 (1) {
["nome"]=>
string(5) "anmsx"
}
object(Exemplo)#1 (1) {
["nome"]=>
string(5) "anmsx"
}
unable to clone... sorry
---------------------------------

Assim, quando a primeira instância fosse clonada, o método __clone da classe iria interromper a execução do script e emitir uma mensagem de erro ( sim, bastante grosseiro, mas é apenas a títuloo de ilustração

Há outros macetes ainda, mas o principal foi demonstrado no artigo
.anmsx
23/08/2006 10:32pm (~17 anos atrás)

Marcos Regis disse:
O artigo do Adler é válido e importante para a comunidade PHP que eh carente de profissionais que conhecem alguma metodologia de desenvolvimento.
Quanto a utilidade de tais métodos estou com você Felipe.
Uma vez disse a um outro programador que as vezes usar o formato OO no PHP era perda de desempenho ao invés de ganho. Não preciso nem dizer que fui esculachado.
Trabalho há exatos 11 meses em um sistema que usa muito MySQL e por ter muitos acessos preferi deixar no formato "antiquado" de um include de conexao. Ao verem meu código riram de mim dizendo que estava "defasado" que jah era hora de usar OO. Caí na besteira de uar uma classe que havia feito extremamente abstrata e que depois a extendia para usar com mysql. Resultado, aumento de 7% no tempo de respostas do site.
Não estou bem certo do motivo mas acho que quando vc tem uma classe com muitos métodos o PHP tem que "parsear" todos eles a cada vez que vc dá um include na classe e aumenta o tempo do processamento do script.
Talvez se um dia o PHP gerar binarios a partir da classe e isso mude um pouco.
Quanto aos hosts acredito que a maioria ainda não esteja em PHP5 porque os repositórios das distros ainda não estão com ele.
Das que jah testei, apenas o UBUNTU tinha PHP5.0+MySQL 5.
Nem mesmo o CENTOS 4.3 tem PHP5.
Claro que eh só instalar manualmente que resolve.
15/08/2006 1:35pm (~17 anos atrás)

Até que ponto o singleton é instancia única MESMO em PHP? Eu estava pensando, e como PHP nao tem o conceito de aplicação, cada hit na pagina é uma "thread" independente, e no PHP esse singleton seria único apenas naquela pagina que um usuario ta acessando na hora, estou certo? Se duas pessoas acessam a pagina simultaneamente, o que acontece são dois "processos" independentes que nao acessariam esse mesmo objeto singleton, mas sim dois objetos independentes. Eu me vi com esse problema pois queria fazer o parsing de um xml e colocar num objeto singleton, com php, por questoes de performance para nao fazer esse bendito parsing toda vez que alguem visitasse a página, mas nao consegui justamente por nao ter um singleton que ficasse realmente na memória.
08/08/2006 12:58pm (~17 anos atrás)

Thiago Santos disse:
Fala, eu estava dando uma lida em singleton no php.net e eu não achei muita aplicatividade, já que muita gente programa estruturado em php e em OO vc pode simplesmente instanciar uma classe e usa-la naquele momento.

vc poderia explicar o que seria essa instancia global?

até mais,

Thiago
01/08/2006 11:33am (~17 anos atrás)

Fala galera, criei a FAQ referente ao assunto...
Vamos postar lá os provedores que conhecemos para que possamos todos começara a usar a nova versão do PHP.

Segue o link:
http://phpbrasil.com/phorum/read.php?f=1&i=88217&t=88217

Att,

Wescley Costa - AKA Narixx
24/07/2006 12:57pm (~17 anos atrás)

Newton Wagner disse:
Acho ótima a idéia. A muito tempo atrás tinha um FAQ que eu abri sobre discussões sobre hosts. Apesar de algumas propagandas que rolaram, acho que o desenvolvimento foi legal.

Poderia abrir um FAQ com as hospedagens de PHP 5. Nacional eu só conheço a locaweb (mas é um pouco mais caro tb).
24/07/2006 12:20pm (~17 anos atrás)

Concordo plenamente Adler...
Mas vamos colocar aqui mesmo?
Será que o pessoal do PHP Brasil não vai achar ruim??
Pois querendo ou nao, será uma propaganda né...

Att,

Wescley Costa
24/07/2006 10:38am (~17 anos atrás)

Adler Medrado disse:
Valeu Wescley!

Eu ia falar exatamente isso que o Wescley falou.
Se não tiver demanda de PHP5 os Hosts não irão colocar mesmo. Existem alguns que possuem a gente poderia até fazer uma lista e disponibilizar aqui, o que acham?
[]s

adler medrado
http://adler.neshertech.net
24/07/2006 10:01am (~17 anos atrás)

Novo Comentário:

(Você pode usar tags como <b>, <i> ou <code>. URLs serão convertidas para links automaticamente.)