Hace mucho que estaba interesado en aprender más sobre patrones de diseño, sobre todo porque a los programadores nos facilitan la vida y queda un código mucho más sencillo y elegante.
El problema principal al que me he enfrentado con este tema ha sido sobre todo a la hora de elegir el patrón a implementar y dónde hacerlo. Decidí utilizar el patrón Observer porque permite vincular acciones de una entidad a otra. En mi caso, quería conseguir que al escribir un comentario, me llegase un correo con la información y se crease, igualmente, una alerta para avisarme en el back.
Antes de empezar me gustaría exponer un par de apuntes: el primero es que al igual que muchos, estoy comenzando en el mundo de los patrones de diseño, si bien llevo varios años sabiendo de su existencia, nunca he tenido la curiosidad/necesidad de implementarlos y ahora, después de algunas entrevistas y algunos quebraderos de cabeza, me estoy metiendo más profundamente en este tema, lo que quiere decir es que soy bastante novato en esto, por lo que si hay alguna incongruencia o matiz que se me escape, agradecería recibir algún comentario para ir corrigiendo y construyendo este post. El segundo apunte es que no pretendo hacer un resumen de todos los patrones ni meterme en detalle sobre la teoría de este patrón elegido en particular, se trataría más bien de una reflexión práctica sobre el uso que le he dado, lo mismo digo, puede que me quede muy corto a la hora de usarlo, por lo que acepto de buen grado cualquier crítica constructiva al respecto.

Observer

El patrón Observer se define como un cambio en el estado de un objeto que implica una notificación en los objetos dependientes, se trata pues de un patrón de comportamiento (existen, además, los de tipo de creación y estructurales).

La idea básica es que tengamos una serie de objetos sujetos u observables y otros objetos observadores, de modo que un cambio en un objeto sujeto produzca una actualización (update) en los objetos observadores.

Para ello, el sujeto será el encargado de asignar uno o varios observadores y tener la posibilidad de eliminarlos, mientras que los observador recibirán una actualización cuando se produzca un cambio en cualquiera de sus sujetos.

Interfaces

A partir de la versión 5.1 de PHP, existen unas interfaces que permiten implementar los métodos necesarios para ello: SplSubject y SplObserver.

SplSubject nos permite implementar los métodos attach( SplObserver $observer ), detach( SplObserver $observer ) y notify( void ).

SplObserver nos permite implementar el método update( SplSubject $subject ).

De modo que al implementar las interfaces en las clases correspondientes tengamos que tener definidos los métodos indicados.

A partir de este momento el proceso es simple: en la clase (o clases) "sujeto" implementamos la interface SplSubject y definimos los métodos necesarios de esta interface.

En mi caso tenemos los siguiente:


class Modelo_Comments implements SplSubject::n::{::n::::t::...::n::::t::protected::t::$observers::t::=::t::array();::n::::t::...::n::::t::/**::n::::t:: * La interface SplSubject nos obliga::n::::t:: * a implementar los siguientes métodos::n::::t::*/::n::::t::public function attach( SplObserver $observer )::n::::t::{::n::::t::::t::$id::t::::t::::t::=::t::tspl_object_hash( $observer );::n::::t::::t::$this-::gt::observers[ $id ]::t::=::t::$observer;::n::::t::}::n::::n::::t::public function detach( SplObserver $observer )::n::::t::{::n::::t::::t::$id::t::=::t::spl_object_hash( $observer );::n::::t::::t::if ( isset( $this-::gt::observers[ $id ] ) )::n::::t::::t::::t::unset( $this-::gt::observers[ $id ] );::n::::t::}::n::::n::::t::public function notify()::n::::t::{::n::::t::::t::foreach ( $this-::gt::observers as $observer )::n::::t::::t::::t::$observer-::gt::update( $this );::n::::t::}::n::::n::::t::public function createComment( $data=array() )::n::::t::{::n::::t::::t::$this-::gt::createRow( $data );::n::::t::::t::$idComent::t::=::t::$this-::gt::save();::n::::t::::t::$this-::gt::idComent::t::=::t::$idComent;::n::::t::::t::$this-::gt::notify();::n::::t::::t::return $idComent;::n::::t::}::n::::t::...

Supongamos que al insertar un comentario queremos que también se cree una alerta. Creamos la clase alerta que implementa la interfaz SplObserver:


class Alerta implements SplObserver::n::{::n::::t::...::n::::t::public function update ( SqlSubject $subject )::n::::t::{::n::::t::::t::$oAlerta::t::=::t::new Modelo_Alerta();::n::::t::::t::$newAlert::t::=::t::$oAlerta-::gt::createRow(::n::::t::::t::::t::array(::n::::t::::t::::t::::t::'texto'::t::=::gt::$subject-::gt::metodoDeTexto(),::n::::t::::t::::t::::t::...//lo que necesitemos para crear la alerta::n::::t::::t::::t::)::n::::t::::t::);::n::::t::::t::$newAlert-::gt::save();::n::::t::}::n::}

Este proceso lo repetimos con tantas clases como entidades queramos mantener en escucha sobre nuestra clase sujeto.

Finalmente nos quedaría añadir el attach o enlace de nuestro sujeto con cada uno de los observadores que queramos:


::t::...::n::::t::$oComment::t::=::t::new Modelo_Comments();::n::::t:://para el observer es necesario atachearlo::n::::t::$oComment-::gt::attach( new Mail() );::n::::t::$oComment-::gt::attach( new Alerta() );::t::...

De momento con esto ya estaría listo para que el cambio en el estado en comentario (cuando se mete un comentario) produce la creación de una alerta (y/o un correo-electrónico y/o lo que creamos necesario).

Si alguno o alguna estáis siguiendo el blog de Michael Pratt, observaréis que para gestionar los observadores utiliza el objeto SplObjectStorage que facilita la adición y la sustracción de los observadores, tan sencillo como añadir el atributo $storage (por ejemplo) a la clase sujeto y pasar en el constructor de la clase una instacia de SplObjetStorage. Este objeto ya tiene implementados los metodos attach y detach con lo que sustituimos nuestros métodos attach y detach por:


::n::::t::protected $storage;::n::::t::...::n::::t::public function __construct( SplObjectStorage $storage )::n::::t::{::n::::t::::t::$this-::gt::storage::t::=::t::$storage;::n::::t::::t::...::n::::t::}::n::::t::...::n::::t::public function attach( SplObserver $observer )::n::::t::{::n::::t::::t::$this-::gt::storage-::gt::attach( $observer );::n::::t::}::n::::n::::t::public function detach( SplObserver $observer )::n::::t::{::n::::t::::t::$this-::gt::storage-::gt::detach( $observer );::n::::t::}


Y en la creación del comentario:

::t::...::n::::t::$oComment::t::=::t::new Modelo_Comments( new SplObjectStorage() );::n::::t:://para el observer es necesario atachearlo::n::::t::$oComment-::gt::attach( new Mail() );::n::::t::$oComment-::gt::attach( new Alerta() );::t::...


Espero que todo haya quedado más o menos entendible ¡espero tus comentarios!

Consulta


* Añado los dos recursos que he utilizado para añadir el patrón en mi código. Si tienes cualquier duda no dudes en dejarlas en tu comentario.

El comentario será validado antes de mostrarse.



Rebeca Rebeca
Creado el 04/03/2015
Nunca he usado ningún patrón de diseño y , como yo, programo en .NET me resultaba difícil de entender, ya que este no es mi lenguaje, pero esta pequeña guía me ha resultado sencilla y me ha ayudado a entender los patrones de diseño un poco más, así que si tengo la ocasión lo utilizaré. Muchas gracias!!!!

afiugud afiugud
Creado el 04/03/2015
Realmente el patrón lo puedes implementar en cualquier programación orientada a objetos que permita herencias múltiples. Lo importante es ver que hay que asignar el rol de sujeto a una o varias entidades y que esas entidades tengan otras entidades que las observan (Observer). El resto es implementar los métodos para añadir o quitar los observadores y el método de actualización cuando algo ocurre en el sujeto. De cualquier modo, si tienes dudas, mándame un email con la duda a mi correo de contacto que verás en el pié de la página. Un saludo.