Em um sistema de informação orientado à objetos continuamente precisamos que um determinado objeto de uma classe, ao executar um de seus métodos, informe a outros objetos esta ação.
Para exemplificar, imagine que uma empresa possui um sistema de controle de produtos e estoque orientado à objetos e necessita que toda vez que o preço de algum de seus produtos mude os representantes sejam informados. Assim sendo, toda vez que o atributo preço de algum produto seja alterado, o produto terá que informar automaticamente todos representantes que seu preço mudou.Para resolver este tipo de problema podemos usar o pattern Observer. Este pattern consiste em termos um objeto OBSERVADO e objetos OBSERVADORES. O objeto observado deverá ter a capacidade de agregar observadores e de notificá-los de alguma mudança.
O PHP5 nativamente possui as interfaces SplObserver e SplSubject. A interface SplSubject serve para implementarmos classes cujos objetos serão observados por outros, por isto esta interface quando implementada nos obriga a criar os métodos públicos attach (para inserir um observador a coleção de observadores), detach (para retirar um observador da coleção) e o mais importante, notify (usado para informar todos observadores presentes na coleção de alguma coisa). Já a interface SplObserver serve para implementarmos observadores. Esta nos obriga a criar o método update (que recebe o objeto observado como parâmetro) disparado pelo objeto observado.
Segue abaixo as interfaces presentes na biblioteca SPL nativa no PHP5:
interface SplObserver
{
function update(SplSubject $subject);
}
interface SplSubject
{
function attach(SplObserver $observer);
function detach(SplObserver $observer);
function notify();
}
Agora vamos resolver um problema real como exemplo de uso do pattern Observer. Neste exemplo teremos uma classe Editora que terá como capacidade principal lançar um livro. Como uma Editora tem clientes esta classe também deverá fornecer a seus objetos a capacidade de agregar clientes para que estes sejam informados automáticamente caso um novo livro seja lançado. Portanto os objetos da classe Editora poderão ser observados pelos clientes (seus observadores). Para isto a classe Editora implementará a interface SplSubject e a classe Cliente implementará SplObserver.
Segue o código:
<?php
/**
* Classe responsável pela criação de editoras
* @author Lucas Soler
* @copyright Copyright 2009 Lucas Soler
* @package www
* @subpackage www
* @version 1.0
*/
class Editora implements SplSubject{
/**
* Coleção de observadores da editora
* @access private
* @name $observadores
*/
private $observadores;
/**
* Recebe o nome do último livro lançado
* @access private
* @name $livroLancado
*/
private $livroLancado;
/**
* Recebe o nome da editora
* @access private
* @name $nome
*/
private $nome;
/**
* Cria uma editora atribuindo um nome a ela e iniciando a coleção de observadores
* @access public
* @param string $nome Nome da editora
* @return void
*/
public function __construct($nome){
$this->observadores = array();
$this->nome = $nome;
}
/**
* Método mágico __set. Atribui valor a qualquer atributo
* @access public
* @param string $nm Nome do atributo
* @param mixed $value Valor para o atributo
* @return void
*/
public function __set($nm,$value)
{
$this->$nm = $value;
}
/**
* Método mágico __get. Retorna o valor de qualquer atributo
* @access public
* @param string $nm Nome do atributo
* @return mixed O valor do atributo passado como parâmetro (pode ser de qualquer tipo)
*/
public function __get($nm)
{
return $this->$nm;
}
/**
* Acrescenta um observador à editora
* @access public
* @param SplObserver $observador Objeto observador a ser inserido na coleção
* @return void
*/
public function attach(SplObserver $observador){
$this->observadores[] = $observador;
}
/**
* Retira um observador da editora
* @access public
* @param SplObserver $observador Objeto observador a ser retirado da coleção
* @return void
*/
public function detach(SplObserver $observador){
$newObservadores = array();
foreach($this->observadores as $key => $ob){
if($ob !== $observador){
$newObservadores[] = $ob;
}
}
$this->observadores = $newObservadores;
}
/**
* Dispara o método "update" de todos observadores
* @access public
* @return void
*/
public function notify(){
foreach($this->observadores as $ob){
$ob->update($this);
}
}
/**
* Lança um livro e informa todos observadores sobre o lançamento
* @access public
* @param string $nomeLivro Nome do livro lançado
* @return void
*/
public function lancarLivro($nomeLivro){
$this->livroLancado = $nomeLivro;
$this->notify();
}
}
/**
* Classe responsável pela criação de clientes
* @author Lucas Soler
* @copyright Copyright 2009 Lucas Soler
* @package www
* @subpackage www
* @version 1.0
*/
class Cliente implements SplObserver{
/**
* Recebe o nome do cliente
* @access private
* @name $nome
*/
private $nome;/**
* Cria um cliente atribuindo a ele um nome
* @access public
* @param string $nome Nome do cliente
* @return void
*/
public function __construct($nome){
$this->nome = $nome;
}
/**
* Método mágico __get. Retorna o valor de qualquer atributo
* @access public
* @param string $nm Nome do atributo
* @return mixed O valor do atributo passado como parâmetro (pode ser de qualquer tipo)
*/
public function __get($nm)
{
return $this->$nm;
}
/**
* Recebe o objeto observado quando este lança um livro e informa características do lançamento
* @access public
* @param SplSubject $objObservado Objeto observado
* @return void
*/
public function update(SplSubject $objObservado){
echo "A editora ".$objObservado->__get("nome")." comunicou a ".$this->nome." em ".date("d/m/Y")." às ".date("H:i:s")." que o livro ".$objObservado->livroLancado." foi lançado<br/>";
}
}
$editora = new Editora("LucasTec");
$lucas = new Cliente("Lucas Soler");
$suemi = new Cliente("Suemi Shimizu");
$tiago = new Cliente("Tiago Soler");
//Inserindo os clientes como observadores da editora
$editora->attach($lucas);
$editora->attach($suemi);
$editora->attach($tiago);
//Caso tenha que retirar um observador da coleção este é o código
//$editora->detach($suemi);
//Editora lança livro e informa os observadores sobre o lançamento
$editora->lancarLivro("Lucas Soler - TI Orientada à Prática");
?>