+4

Utilzando o Design Pattern OBSERVER utilizando as interfaces SPL (STANDARD PHP LIBRARY)

criado por Marcus Brasizza em 11/09/2008 6:54am
Definição:

A utilização é um objeto que será observado por um ou mais objetos , que são os observadores, que obviamente são associados como observadores deste objeto, e quando o observador disparar um método de notificação, todos os objetos associados (os observadores) executarão cada um sua rotina específica, assim fazendo com que o processo seja organizado e evitando colocar instancias de objetos dentro de outros objetos ou até mesmo um aninhado de instancias para cada objeto que irá executar algo.

Utilização:

Utilizarem duas interfaces já disponíveis no PHP chamadas de SplObserver e SplSubject, disponíveis dês do começo do PHP 5.1.xxxx

- Interface SplSubject: é a interface que será implementada no Observado. contém os metodos attach e detach e notify
-- attach: Adiciona observadores ao objeto.
-- detach: Remove observadores.
-- notify: Envia a notificação para todos os observadores do objeto.

- Interface SplObject: é a interface do observador. contém somente o método update.

NOTA: Todos os métodos dessas interfaces, são explícitos em relação aos métodos, chamados de type-hint, ou seja , para o método attach só deve ser enviado como parâmetro um objeto do tipo SplObserver, o mesmo para o detach, tudo isso existe uma documentação, e vou colocar um código de exemplo.

Desenvolvendo:

Vamos primeiro criar a classe que será observada pelos demais objetos, chamaremos ela de Materia

<?php
class Materia implements SplSubject
{
    private $nome_materia;
    // Os observadores  é um array onde ele guardará todas as instâncias dos objetos que forem adicionados
    private $_observadores = array();
    // Coloquei um array de log só para mostrar o funcionamento do Observer
    private $_log = array();

    // Encapsuladores 
    public function GET_materia()
    {
        return $this->nome_materia;
    }

    function SET_log($valor)
    {
        $this->_log[] = $valor ;
    }

    function GET_log()
    {
        return $this->_log;
    }

    // Função construtora que irá somente associar um nome ao professor
    function __construct($nome)
    {
        $this->nome_materia = $nome;
        $this->_log[] = " Materia $nome foi incluida com sucesso";
    }

    // Implementa o método attach do SplSubject, enviando como parâmetro um observador e associando ao array de objetos
    public function attach(SplObserver $classes)
    {
        $this->_classes[] = $classes;
        $this->_log[] = " O ".$classes->GET_tipo()." ".$classes->GET_nome()." foi adicionado";
    }

    // Implementa o método detach do SplSubject, que somente remove a instância do objeto 
    public function detach(SplObserver $classes)
    {
        foreach ($this->_classes as $key => $obj){
            if ($obj == $classes){
                unset($this->_classes[$key]);
                $this->_log[] = " O ".$classes->GET_tipo()." ".$classes->GET_nome()." foi removido";
            }
        }
    }

    // Implementa o método notify do SplSubject, que percorre todo o array de observadores e chama  o método update de do observador, que esta implementado dentro da sua classe 
    public function notify()
    {
        foreach ($this->_classes as $classes){
            $classes->update($this);
        }
    }
}
?>

Feito isso, criaremos uma classe de Professor.

<?php
class Professor implements SplObserver
{
    protected $tipo = "Professor";
    private $nome;
    private $endereco;
    private $telefone;
    private $email;

    // Encapsulador
    public function GET_tipo()
    {
        return $this->tipo;
    }

    public function GET_nome()
    {
        return $this->nome;
    }

    public function GET_email()
    {
        return $this->email;
    }

    public function GET_telefone()
    {
        return $this->nome;
    }

    // Construtor que somente associa o nome do  professor
    function __construct($nome)
    {
        $this->nome = $nome;
    }

    // Implementa o método update do SplObserver, que executa o método de acordo com a necessidade do objeto Professor
    public function update(SplSubject $object)
    {
        $object->SET_log("Vindo de ".$this->nome.": Dou aula de ".$object->GET_materia());
    }
}
?>

Criaremos agora uma classe de Aluno.

<?php
class Aluno implements SplObserver
{
    protected $tipo = "Aluno";
    private $nome;
    private $endereco;
    private $telefone;
    private $email;

    // Encapsulador
    public function GET_tipo(){
        return $this->tipo;
    }

    public function GET_nome()
    {
        return $this->nome;
    }

    public function GET_email()
    {
        return $this->email;
    }

    public function GET_telefone()
    {
        return $this->nome;
    }

    // Construtor que somente associa o nome do  aluno
    function __construct($nome)
    {
        $this->nome = $nome;
    }

    // Implementa o método update do SplObserver, que executa o método de acordo com a necessidade do objeto Aluno
    public function update(SplSubject $object)
    {
        $object->SET_log("Vindo de ".$this->nome.": Sou aluno de ".$object->GET_materia());
    }

}
?>

Com as classes criadas, iremos associar os observadores na classe observada e depois notificaremos.

<?php
require_once("professor.class.php");
require_once("aluno.class.php");
require_once("materia.class.php");

$materia = new Materia("Matematica");
$marcus = new Professor("Marcus");
$rafael = new Aluno("Rafael");
$vinicius = new Aluno("Vinicius");
// Associando os observadores no objeto Materia Matemática
$materia->attach($rafael);
$materia->attach($vinicius);
$materia->attach($marcus);

$materia2 = new Materia("Portugues");
$renato = new Professor("Renato");
$fabio = new Aluno("Fabio");
$tiago = new Aluno("tiago");
// Associando os observadores no objeto Materia Portugues
$materia2->attach($renato);
$materia2->attach($vinicius);
$materia2->attach($fabio);
$materia2->attach($tiago);

// Removendo Alunos da Materia 1
$materia->detach($rafael);

// Notificando as 2 Matérias
$materia->notify();
$materia2->notify();

echo "Materia 1 <br>";
echo "<pre>";
print_r($materia->GET_log());
echo "</pre>";
echo "<hr>";
echo "Materia 2 <br>";
echo "<pre>";
print_r($materia2->GET_log());
echo "</pre>";
?>

O output esperado é esse:

Materia 1 

Array
(
    [0] =>  Materia Matematica foi incluida com sucesso
    [1] =>  O Aluno Rafael foi adicionado
    [2] =>  O Aluno Vinicius foi adicionado
    [3] =>  O Professor Marcus foi adicionado
    [4] =>  O Aluno Rafael foi removido
    [5] => Vindo de Vinicius: Sou aluno de Matematica
    [6] => Vindo de Marcus: Dou aula de Matematica
)

--------------------------------------------------------------------------------
Materia 2 

Array
(
    [0] =>  Materia Portugues foi incluida com sucesso
    [1] =>  O Professor Renato foi adicionado
    [2] =>  O Aluno Vinicius foi adicionado
    [3] =>  O Aluno Fabio foi adicionado
    [4] =>  O Aluno tiago foi adicionado
    [5] => Vindo de Renato: Dou aula de Portugues
    [6] => Vindo de Vinicius: Sou aluno de Portugues
    [7] => Vindo de Fabio: Sou aluno de Portugues
    [8] => Vindo de tiago: Sou aluno de Portugues
)

Esse é um exemplo simples de como utilizar um observer sem precisar colocar uma instância do professor e aluno dentro da matéria. Pode ser feito muito mais com isso, como por exemplo um observer que ao cadastrar uma pessoa, envia um email para o admin. Use a imaginação e espero que gostem
Qualquer dúvida, crítica, sugestão ou incentivo para escrever mais artigos, por favor envie um comentário abaixo.

Abraços!

Comentários:

Mostrando 1 - 2 de 2 comentários
Sérgio disse:
Cara fiquei olhando essa classe e pelo que vi as SPL não agrega muita coisa, a única coisa que tá fazendo nas classes que faz diferença é o encapsulamento, os restante é só fazer

<?php
class Observer {
}

classe Subject {
}

?>

Que vai funcionar igual...
19/07/2012 9:19am (~4 anos atrás)

Alex Weber disse:
Ótimo artigo!
Não sabia que o SPL possui interfaces para o Observer, bom saber!!

Abs,

Alex
25/05/2009 5:18pm (~7 anos atrás)

Novo Comentário:

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