Foros del Web » Programando para Internet » PHP » Frameworks y PHP orientado a objetos »

Opiniones sobre el patrón de Invocación Implicita

Estas en el tema de Opiniones sobre el patrón de Invocación Implicita en el foro de Frameworks y PHP orientado a objetos en Foros del Web. Hola, En estos días estaba pensando en un proyecto y no veía de forma clara cuál era la mejor forma de atacarlo... así que me ...
  #1 (permalink)  
Antiguo 21/03/2008, 07:25
Avatar de pragone  
Fecha de Ingreso: diciembre-2007
Ubicación: Madrid
Mensajes: 223
Antigüedad: 16 años, 4 meses
Puntos: 2
Pregunta Opiniones sobre el patrón de Invocación Implicita

Hola,

En estos días estaba pensando en un proyecto y no veía de forma clara cuál era la mejor forma de atacarlo... así que me fui a revisar los conceptos de arquitectura de software en la wiki y me encontré de nuevo con el patrón de invocación implícita.

Este patrón, también conocido como orientado a eventos, funciona de forma diferente a los patrones comunes en el sentido de que las acciones de los usuarios generan eventos no acciones, y luego tienes a "listeners" que se interesan por ciertos eventos y realizan sus acciones dependiendo del evento.
Es un estilo bastante común en desarrollo de aplicaciones stand-alone.

Me han parecido muy interesantes las bondades que un esquema así podría tener para el desarrollo evolutivo de un producto. El hecho de que añadir funcionalidades puede significar no tener que tocar el código que ya tienes resulta muy atractivo.

Un ejemplo de esto:
La página que recibe los datos para agregar un registro a un BD.
Bajo un modelo MVC tradicional la petición del navegador se enruta a una acción. Esta acción hace sus validaciones, invoca a la capa de BD y luego deja el trabajo a la capa View para que de la confirmación.

Bajo un modelo orientado a eventos la petición del navegador llega igualmenta a un controlador que la enruta a un evento. El evento es agregado a la cola de eventos y el listener que se interesa por ese evento es invocado. Luego de que este listener hace sus validaciones e ingresa el registro, lanza otro evento: "Registro Agregado". Este evento es "escuchado" por otro listener que es el que presenta la confirmación.

Ambos hacen lo mismo, sin embargo, aquí es donde veo lo interesante. Queremos agregarle la funcionalidad de que se envíe un correo al "dueño" del sistema notificándole la agregación del registro.
En el modelo MVC tradicional esto implica modificar la acción para que adicionalmente se ejecute el código que registra/envía el correo.
En el modelo basado en eventos, basta con crear otro "listener" que se interese por el evento "Registro Agregado" y que se encargue de enviar el correo.

¿La diferencia?, en el segundo caso no hemos tenido que tocar código ya existente y que bien podría introducir nuevos "bugs",

Más aún, Si en el primer caso queremos deshabilitar el envío de correos temporalmente tendríamos que programar un interruptor para esta funcionalidad, en el caso de orientado a eventos basta con des-registrar el listener (un archivo de configuración).

He buscado un framework que me permita desarrollar bajo este patrón en PHP y no lo he conseguido así que:

¿Alguien conoce un framework PHP que funcione bajo un patrón de invocación implicita o orientado a eventos?

Yo ya he comenzado a trabajar en uno... es un híbrido entre MVC y orientado a eventos... y ya tengo una versión mejorada del "hola mundo" funcionando
__________________
pragone
Blog: Desarrollo, comunidad y monetización
Últimos artículos: Tips de Smarty
  #2 (permalink)  
Antiguo 21/03/2008, 08:18
 
Fecha de Ingreso: noviembre-2003
Mensajes: 798
Antigüedad: 20 años, 5 meses
Puntos: 8
Re: Opiniones sobre el patrón de Invocación Implicita

Estudia el patrón de Diseño Observador.

Aquí te dejo unos links para PHP5
Link 1
Link 2

Saludos.
  #3 (permalink)  
Antiguo 21/03/2008, 08:25
Avatar de pragone  
Fecha de Ingreso: diciembre-2007
Ubicación: Madrid
Mensajes: 223
Antigüedad: 16 años, 4 meses
Puntos: 2
Re: Opiniones sobre el patrón de Invocación Implicita

Gracias zsamer

Excelentes enlaces... lo estoy leyendo... luego pongo comentarios.
__________________
pragone
Blog: Desarrollo, comunidad y monetización
Últimos artículos: Tips de Smarty
  #4 (permalink)  
Antiguo 21/03/2008, 09:42
Avatar de pragone  
Fecha de Ingreso: diciembre-2007
Ubicación: Madrid
Mensajes: 223
Antigüedad: 16 años, 4 meses
Puntos: 2
Re: Opiniones sobre el patrón de Invocación Implicita

Ok.... aquí algunos comentarios.

En esencia lo que he descrito sigue el patron "Observer", con una diferencia.

El patrón Observer funciona a nivel de objetos, es decir, a nivel de instancias y no de clases. Esto es perfecto cuando estás trabajando en una aplicación stand-alone pues todos tus objetos se encuentran en memoria, todos han sido instanciados.
Sin embargo, en una aplicación web, cada request inicia la aplicación sin instancias, por lo que cargar todo el conjunto de objetos podría resultar (sobre todo en aplicaciones complejas) imposible por razones de performance y escalabilidad.

La diferencia entre el Observer y este método al que hago referencia es que la definición de las interacciones no se define a nivel de instancias, sino de clases. De esta forma, puedo evitar la carga de instancias no necesarias en el cumplimiento de un request.

Supongo que una forma de atajar este problema sería que cada clase tuviera un cierta lógica para cargar las instancias apropiadas para poder responder a un evento/cambio de estado particular.

Lo voy a pensar a ver qué se me ocurre.
__________________
pragone
Blog: Desarrollo, comunidad y monetización
Últimos artículos: Tips de Smarty
  #5 (permalink)  
Antiguo 21/03/2008, 09:52
Avatar de GatorV
$this->role('moderador');
 
Fecha de Ingreso: mayo-2006
Ubicación: /home/ams/
Mensajes: 38.567
Antigüedad: 18 años
Puntos: 2135
Re: Opiniones sobre el patrón de Invocación Implicita

Hola pragone,

En si lo que indicas es correcto, pero sigue siendo de parte del patrón Observer. Lo que tu como programador tendrias que agregar es la logica para decidir que archivos cargar y que archivos no cargar.

El mejor ejemplo de esto es el tradicional sistema de "plugins", donde tu desde un archivo, bd, etc. defines que "plugins" tiene tu aplicación, y tu aplicación lee estos plugins y realiza las acciones correspondientes.

Espero con esto darte un ejemplo de como puedes encarar el problema de cargar los listeners al observer.

Saludos.
  #6 (permalink)  
Antiguo 21/03/2008, 10:24
Avatar de pragone  
Fecha de Ingreso: diciembre-2007
Ubicación: Madrid
Mensajes: 223
Antigüedad: 16 años, 4 meses
Puntos: 2
Re: Opiniones sobre el patrón de Invocación Implicita

Hola GatorV.

Gracias... lo tendré en cuenta.

De hecho, esto es lo que se me ocurre:
¿Y si el manejo de notificaciones se hiciera a través de un mediador?.
Es decir, crear una clase "Messenger" que sea singleton y que sea la encargada de registrar quién observa qué y cuando algo suceda notificarlo a los objetos apropiados?

De hecho, se me ocurre un enfoque doble:
- StaticListeners: Son clases singleton o simplemente procedimientos estáticos que escuchan por un evento particular, independientemente de quién lo haya generado
- Listeners (Estos serían los que usarían la clase Messenger): Son objetos (instancias) que escuchan por un evento particular originado de otra instancia particular.

La idea sería que cuando un objeto notifica algo, el Messenger identifica a todos los interesados (estáticos o no) y se los comunica. En el caso de listeners no estáticos los instancia primero.

El concepto de Listeners estáticos serviría para cuando una acción se debe tomar siempre que ocurra un evento específico, independientemente de quién la origina (por ejemplo, enviar un correo de notificación, etc)... así no es necesario registrar un objeto que esté interesado en la ocurrencia de un evento en todas las instancias que puedan generar ese evento.

¿Qué les parece?
__________________
pragone
Blog: Desarrollo, comunidad y monetización
Últimos artículos: Tips de Smarty
  #7 (permalink)  
Antiguo 21/03/2008, 10:37
Avatar de GatorV
$this->role('moderador');
 
Fecha de Ingreso: mayo-2006
Ubicación: /home/ams/
Mensajes: 38.567
Antigüedad: 18 años
Puntos: 2135
Re: Opiniones sobre el patrón de Invocación Implicita

Se escucha bien, aunque como te digo, todo esto esta basado para una idea principal: "plugins", si ves por decir un foro como vBulletin, en muchas partes de su código tiene algo así:
Código PHP:
($hook vBulletinHook::fetch_hook('showthread_getinfo')) ? eval($hook) : false
Con eso ellos implementan los plugins en su base de datos, y evaluan codigo PHP que se indique (claro esto no es el ideal), pero podrias hacer algo similar:
Código PHP:
// código php...
Messenger::notify'new_customer'$this );
// más código php 
Luego el Messenger se encargara de obtener todos los "observers" que tengan el evento new_customer registrados y pasarles la instancia de la clase actual por ejemplo para que sepan que hacer con la información.

Saludos.
  #8 (permalink)  
Antiguo 21/03/2008, 12:12
Avatar de pragone  
Fecha de Ingreso: diciembre-2007
Ubicación: Madrid
Mensajes: 223
Antigüedad: 16 años, 4 meses
Puntos: 2
Re: Opiniones sobre el patrón de Invocación Implicita

Exacto...

Llamarlo plugin o no es una cosa de nomenclatura... pero es precisamente lo que dices... Estoy trabajando en una pequeña prueba de concepto... Ahora que la termine la subo para que me digan qué les parece.
__________________
pragone
Blog: Desarrollo, comunidad y monetización
Últimos artículos: Tips de Smarty
  #9 (permalink)  
Antiguo 21/03/2008, 14:27
Avatar de pragone  
Fecha de Ingreso: diciembre-2007
Ubicación: Madrid
Mensajes: 223
Antigüedad: 16 años, 4 meses
Puntos: 2
Re: Opiniones sobre el patrón de Invocación Implicita

Ok... ya tengo un prototipo funcionando.

Para esto he utilizado aquel framework de ActiveRecord que había creado "PHP5DbObject".

El ejemplo que he utilizado: Un torneo (uno muy corto).

En mi BD he creado tres tablas:
Código PHP:
desc cup;
+--------+--------------+------+-----+---------+----------------+
Field  Type         Null Key | Default | Extra          |
+--------+--------------+------+-----+---------+----------------+
id     int(11)      | NO   PRI NULL    auto_increment |
name   varchar(255) | NO   |     |         |                |
winner int(11)      | YES  |     | NULL    |                |
+--------+--------------+------+-----+---------+----------------+

desc game;
+------------+--------------+------+-----+---------+----------------+
Field      Type         Null Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
id         int(11)      | NO   PRI NULL    auto_increment |
cup_id     int(11)      | NO   |     |         |                |
play_date  datetime     YES  |     | NULL    |                |
team_home  int(11)      | YES  |     | NULL    |                |
team_visit int(11)      | YES  |     | NULL    |                |
winner     int(11)      | YES  |     | NULL    |                |
result     varchar(255) | YES  |     | NULL    |                |
+------------+--------------+------+-----+---------+----------------+

desc listen;  // Utilizada por el Messenger
+------------+--------------+------+-----+---------+----------------+
Field      Type         Null Key | Default | Extra          |
+------------+--------------+------+-----+---------+----------------+
id         int(11)      | NO   PRI NULL    auto_increment |
event_name varchar(255) | NO   |     |         |                |
source     varchar(255) | NO   |     |         |                |
interested varchar(255) | NO   |     |         |                |
params     text         NO   |     |         |                |
+------------+--------------+------+-----+---------+----------------+ 
Luego he creado este par de clases: Cup y Game... cada una extiende de la clase de acceso a DB que PHP5DBObject crea CupDB y GameDB respectivamente.

Código PHP:

class Game extends GameDB {
    public function 
setHomeAsWinnerOf(&$game) {
        
Messenger::getInstance()->interestedIn('WinnerOfGame'$game$this, array('setas' => 'home'));
    }
    
    public function 
setVisitorAsWinnerOf(&$game) {
        
Messenger::getInstance()->interestedIn('WinnerOfGame'$game$this, array('setas' => 'visitor'));
    }
    
    public function 
setWinnerAs($team) {
        if (
$team == 'home'$this->winner $this->team_home;
        else 
$this->winner $this->team_visit;
        print 
"<hr><strong>The $team team won game " $this->id "!!</strong>";
        
$this->update();
        
Messenger::getInstance()->notify('WinnerOfGame'$this);
        
Messenger::getInstance()->notify('LooserOfGame'$this);
    }
    
    public function 
setHomeTeam($team) {
        print 
"Now the home team of game " $this->id " is " $team "<br>";
        
$this->team_home $team;
        
$this->update();
    }
    
    public function 
setVisitorTeam($team) {
        print 
"Now the visitor team of game " $this->id " is " $team "<br>";
        
$this->team_visitor $team;
        
$this->update();
    }
    
    public function 
listener($event, &$source$params = array()) {
        switch(
$event) {
            case 
'WinnerOfGame':
                print 
"Game id: " $this->id " is being notified of a winner.<br>";
                print 
"It's the result of game: " $source->id " and it's team " $source->getWinner() . "<br>";
                print 
"Params : <pre>" print_r($paramstrue) . "</pre>";
                if (
$params['setas'] == 'home'$this->setHomeTeam($source->winner);
                else 
$this->setVisitorTeam($source->winner);
        }
    }
}


class 
Cup extends CupDB {
    public function 
setFinal(&$game) {
        
Messenger::getInstance()->interestedIn('WinnerOfGame'$game$this, array(''));
    }
    
    public function 
listener($event, &$source$params = array()) {
        switch(
$event) {
            case 
'WinnerOfGame':
                print 
"Cup id: " $this->id " is being notified of a winner.<br>";
                print 
"It's the result of game: " $source->id " and it's team " $source->getWinner() . "<br>";
                print 
"Params : <pre>" print_r($paramstrue) . "</pre><br>";
                
$this->winner $source->winner;
                print 
"<H1>Congratulations team " $this->winner ". Yuo Won the <em>" $this->name "</em></h1>";
                
$this->update();
        }
    }

Y finalmente mi clase Messenger. Esta clase aprovecha las bondades de PHP5DBObject para cargar la instancia del objeto que debe ser notificado con el método PHP5DBObject::factory($class_name, $id);

Código PHP:

class Messenger {
    protected static 
$instance null;
    
    public static function &
getInstance() {
        if (!
is_object(self::$instance)) {
            
self::$instance = new Messenger();
        }
        return 
self::$instance;
    }
    
    function 
__construct() {
        if (
is_object(self::$instance)) throw new Exception(__CLASS__ ' is singleton. You shouldn\'t call it directly but using the static method ' __CLASS__ '::getInstance()');
    }
    
    function 
notify($event_name, &$source) {
        print 
"<hr>";
        print 
"Notifying of event $event_name by " get_class($source) . '@' http_build_query($source->getArrayId()) . "<br>";
        
$listeners Listen::browse('event_name=? AND source=?', array($event_nameget_class($source) . '@' http_build_query($source->getArrayId())));
        if (
count($listeners) == 0) {
            print 
"Who cares...  :P <br>";
            return;
        }
        foreach(
$listeners as $listener) {
            list(
$class$id) = split('@'$listener->interested2);
            
parse_str($listener->params$params);
            
parse_str($id$array_id);
            
$null null;
            
$obj =& PHP5DBObject::factory($class$array_id$null);
            
$obj->listener($event_name$source$params);
        }
    }
    
    function 
interestedIn($event_name, &$source, &$interested$params = array()) {
        
$l = new Listen();
        
$l->event_name $event_name;
        
$l->source get_class($source) . '@' http_build_query($source->getArrayId());
        
$l->interested get_class($interested) . '@' http_build_query($interested->getArrayId());
        if (
count($params) == 0$l->params '';
        else 
$l->params http_build_query($params);
        
$l->create();
    }


Hecho todo esto, Este es el php que ejecuto.... que pico en varias partes para que se vea su funcionamiento:

Código PHP:

// Incluimos lo necesario
$cup = new Cup();
$cup->setName("TEST Cup");
$cup->create();

$game1 = new Game();
$game1->setCup($cup);
$game1->setPlay_date('2008-03-20');
$game1->setTeam_home(1);
$game1->setTeam_visit(2);
$game1->create();

$game2 = new Game();
$game2->setCup($cup);
$game2->setPlay_date('2008-03-20');
$game2->setTeam_home(3);
$game2->setTeam_visit(4);
$game2->create();

$game3 = new Game();
$game3->setCup($cup);
$game3->setPlay_date('2008-03-21');
$game3->create();
$id $game3->id
Esta primera fase es de inicialización. La BD está así en este punto:

Código PHP:

select 
from game;
+----+--------+---------------------+-----------+------------+--------+--------+
id cup_id play_date           team_home team_visit winner result |
+----+--------+---------------------+-----------+------------+--------+--------+
|  
|      2008-03-20 00:00:00 |         |          |   NULL NULL   |
|  
|      2008-03-20 00:00:00 |         |          |   NULL NULL   |
|  
|      2008-03-21 00:00:00 |      NULL |       NULL |   NULL NULL   |
+----+--------+---------------------+-----------+------------+--------+--------+

select from cup
+----+----------+--------+
id name     winner |
+----+----------+--------+
|  
TEST Cup |   NULL |
+----+----------+--------+ 
Luego establecemos los observers:

Código PHP:

$game3
->setHomeAsWinnerOf($game1);
$game3->setVisitorAsWinnerOf($game2);

$cup->setFinal($game3); 
Para este punto la tabla "listen" ya tiene cargado a los observers:

Código PHP:

select 
from listen
+----+--------------+-----------+------------+---------------+
id event_name   source    interested params        |
+----+--------------+-----------+------------+---------------+
|  
WinnerOfGame Game@id=Game@id=3  setas=home    |
|  
WinnerOfGame Game@id=Game@id=3  setas=visitor |
|  
WinnerOfGame Game@id=Cup@id=1   0=            |
+----+--------------+-----------+------------+---------------+ 
y finalmente, Se juega el torneo :)

Esto lo pongo en el siguiente Post. Que aquí se me acabó el espacio.
__________________
pragone
Blog: Desarrollo, comunidad y monetización
Últimos artículos: Tips de Smarty
  #10 (permalink)  
Antiguo 21/03/2008, 14:28
Avatar de pragone  
Fecha de Ingreso: diciembre-2007
Ubicación: Madrid
Mensajes: 223
Antigüedad: 16 años, 4 meses
Puntos: 2
Re: Opiniones sobre el patrón de Invocación Implicita

Código PHP:

$game1
->setWinnerAs('home');
$game2->setWinnerAs('visitor');

$game3 =& Game::factory($id);
$game3->setWinnerAs('home'); 
Así queda finalmente la BD:

Código PHP:

select 
from cup
+----+----------+--------+
id name     winner |
+----+----------+--------+
|  
TEST Cup |      |
+----+----------+--------+

select from game
+----+--------+---------------------+-----------+------------+--------+--------+
id cup_id play_date           team_home team_visit winner result |
+----+--------+---------------------+-----------+------------+--------+--------+
|  
|      2008-03-20 00:00:00 |         |          |      NULL   |
|  
|      2008-03-20 00:00:00 |         |          |      NULL   |
|  
|      2008-03-21 00:00:00 |         |          |      NULL   |
+----+--------+---------------------+-----------+------------+--------+--------+ 
Y este es el output del programa:

The home team won game 1!!
---------------------------------------------------------------------------------
Notifying of event WinnerOfGame by Game@id=1
Game id: 3 is being notified of a winner.
It's the result of game: 1 and it's team 1
Params :

Array
(
[setas] => home
)

Now the home team of game 3 is 1
---------------------------------------------------------------------------------
Notifying of event LooserOfGame by Game@id=1
Who cares... :P
---------------------------------------------------------------------------------
The visitor team won game 2!!
---------------------------------------------------------------------------------
Notifying of event WinnerOfGame by Game@id=2
Game id: 3 is being notified of a winner.
It's the result of game: 2 and it's team 4
Params :

Array
(
[setas] => visitor
)

Now the visitor team of game 3 is 4
---------------------------------------------------------------------------------
Notifying of event LooserOfGame by Game@id=2
Who cares... :P
---------------------------------------------------------------------------------
The home team won game 3
---------------------------------------------------------------------------------
!!Notifying of event WinnerOfGame by Game@id=3
Cup id: 1 is being notified of a winner.
It's the result of game: 3 and it's team 1
Params :

Array
(
[0] =>
)


Congratulations team 1. Yuo Won the TEST Cup
---------------------------------------------------------------------------------
Notifying of event LooserOfGame by Game@id=3
Who cares... :P
__________________
pragone
Blog: Desarrollo, comunidad y monetización
Últimos artículos: Tips de Smarty
  #11 (permalink)  
Antiguo 21/03/2008, 14:28
Avatar de pragone  
Fecha de Ingreso: diciembre-2007
Ubicación: Madrid
Mensajes: 223
Antigüedad: 16 años, 4 meses
Puntos: 2
Re: Opiniones sobre el patrón de Invocación Implicita

Por cierto,

¿Qué les parece?
__________________
pragone
Blog: Desarrollo, comunidad y monetización
Últimos artículos: Tips de Smarty
  #12 (permalink)  
Antiguo 22/03/2008, 15:20
Avatar de GatorV
$this->role('moderador');
 
Fecha de Ingreso: mayo-2006
Ubicación: /home/ams/
Mensajes: 38.567
Antigüedad: 18 años
Puntos: 2135
Re: Opiniones sobre el patrón de Invocación Implicita

Mmm es un ejemplo un tanto rebuscado, (de leer), pero creo cumple con lo que propones, yo estuve pensando en otro ejemplo, pronto lo publicare.

Saludos.
  #13 (permalink)  
Antiguo 14/05/2008, 18:52
Avatar de vmontesino  
Fecha de Ingreso: octubre-2007
Ubicación: San Salvador
Mensajes: 21
Antigüedad: 16 años, 6 meses
Puntos: 0
Re: Opiniones sobre el patrón de Invocación Implicita

No se como sera el patron del observador. Tendre que leerlo para ver que utilidad tiene en mi trabajo.
Sin embargo, retomando el tema del patron que comentaba en sus primeros post pragone, me resulto bastante interesante el patron del visitante (Visitor_pattern) , no se si lo conocen. creo que se apega a las caracteristicas que él mencionaba.

http://en.wikipedia.org/wiki/Visitor_pattern

No se que tan factible y funcional sea para aplicaciones web, sin embargo me parecio bastante bueno... sera nada mas de practicarlo un poco.
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 19:06.