SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 27
  1. #1
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)

    HOW TO: Use event driven classes

    Ok, I've seen a lot of articles on this on the net recently and found that most were overly complex and quite involved when it came to implementing the solutions. The problem seemed to be in the programmers efforts to mimic the abilities of other languages without actually looking at the problem. The problem in this case is that PHP classes cannot raise events that listening classes can respond to. Or can they? The trick is in realizing the limitation mentioned earlier and stop trying to do things the other guys way. Instead, a listening class needs a way a telling the event raising class what to call instead of relying on the listening class to respond directly. After some thought, I have developed a base class, with only two simple functions, that will give your classes the interopability you want. Click the first link below to see the output of a sample script, then click the second to see the actual code for the example. The key class to focus on is the EventBroker class. It does two things for those classes that derive from it. The first is to allow the derived class the ability to specify, in it's constructor, what events the instance is to support. Second, it allows other class instances to register a callback for each event type it wants to respond to. The EventBroker supports an unlimited amount of events and associated callbacks. Many seperate class instances, of differing types, can simultaneously listen to any one, or multiple, events supported by the derived class. The sample and source were kept simple for the sake of brevity. Feel free to add logical checks and throw exceptions where required as per your development needs. Please post your impression of this solution here and let me know if you plan on using it.

    Thanks for your time, Drew

    EventBroker Sample
    EventBroker Source

    NOTE: PHP5 SYNTAX

  2. #2
    SitePoint Enthusiast
    Join Date
    Oct 2004
    Posts
    88
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,
    Recently i tried to implement a event driven mechanism for a framework. The goal was to be able to fire events from anywhere of the system without taking care to include the required class files.

    Here a simplified example of the principle.

    PHP Code:
    <?php

    // event handler of the module 1
    function module1$data )
    {
        echo 
    'The module 1 received the event: ' $data '<br>';
    }

    // event handler of the module 2
    function module2$data )
    {
       echo 
    'The module 2 received the event: ' $data '<br>';
    }

    // event handler of the module 3
    function module3$data )
    {
       echo 
    'The module 3 received the event: ' $data '<br>';
    }

    // here the name of the event handler functions were registered
    $registered_modules = array(  'MOD_1' => 'module1',
                                  
    'MOD_2' => 'module2',
                                  
    'MOD_3' => 'module3');

    // directed event distributor 
    function M$module$data )
    {
        global 
    $registered_modules;

        
    // call back a module event handler
        
    $registered_modules $module ] ( $data );
    }

    // broadcast event distributor
    function B$data )
    {
        global 
    $registered_modules;

        
    // call back all registered modules event handlers
        
    foreach ($registered_modules as $alias => $module)
        {
            
    $module $data );
        }
    }

    // fire some directed events (to a single event handler function)
    M'MOD_1''Gemini' );
    M'MOD_2''Alpha' ) ;
    M'MOD_3''Beta' ) ;

    // fire a broadcast event (to all event handler functions)
    B'BROADCAST' ) ;

    ?>
    The extended implementation of this framework includes from each module an event handler function during the system initialisation. The job of those functions is to load dynamically an action class, which finally perform on the event call.

    Here an example of the event handler of an article module:


    PHP Code:
    <?php

    // Name of the event handler
    define 'MOD_ARTICLE' 'article');

    // register this handler                       
    $B->register_handlerMOD_ARTICLE,
                          array ( 
    'module'       =>MOD_ ARTICLE,
                                  
    'event_handler'=>'article_event_handler') );
                                                                              
    // The handler function of the article module
    function article_event_handler$evt )
    {
        global 
    $B;

        
    // build the whole class name
        
    $class_name 'article_'.$evt['code'];    
        
        
    // check if this object was previously declared
        
    if(!is_object($B->$class_name))
        {
            
    // dynamic load the required class
            
    $class_file=SF_BASE_DIR.'modules/article/actions/class.'.$class_name.'.php';
            if(
    file_exists($class_file))
            {
                include_once(
    $class_file);
                
    // make instance
                
    $B->$class_name = & new $class_name();
                
    // perform on the event call
                
    return $B->$class_name->perform$evt['data'] );
            }
        }
        else
        {
            
    // perform on the event if the requested object exists
            
    return $B->$class_name->perform$evt['data'] );
        }
        return 
    TRUE;
    }

    ?>
    Here an example of a possible event call to such a handler:

    PHP Code:
    $B->MMOD_ARTICLE,
           
    'get',
           array(
    'var'        => 'tpl_article',
                 
    'article_id' => $_REQUEST['article_id'],
                 
    'fields'     => array('title','body'))); 
    The first argument is the module to which the event is directed. The second argument defines the action class, which should perform on this event. The third argument is an array with data the action class needs to execute this event call.The job of this specific event call is to store from an article the title and body in the array $B->tpl_article.

    The api of such a module concept consist of several action classes, which are only loaded on demand.

    As mentioned, such event calls can be fired from anywhere of the system. Even in action classes, which performs on events. So it is possible to build applications in a lego like style, but without bother to include the required class packages.


    Here the framework

  3. #3
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I don't quite get the "event driven" paradigm. All php apps map "events" ie http requests to client-ouput and non-client actions. Can you explain the event-driven architecture a bit more and why this is different?

    Going on your description rather than the code, the EventBroker superclass & etc sounds like a system to create TransactionScripts using an observable FrontController. Is that correct?

    Using inheritance to glue classes together makes me feel twitchy. An EventBroker object might be extended with something which defines a brokering strategy but not, IMO, with an event handler since this has a whole different responsibility http://c2.com/cgi/wiki?CouplingAndCohesion.

  4. #4
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    atu,

    While I commend the simplicity of your design, it isn't an object oriented one. It also uses globals which I avoid at all costs. The more complex apps are primarily class based. My example builds on the Observer class pattern wich can be easily introduced into any existing class based application.

    By building application using an object hierarchy, together with the new 'magic' functions, property get and set, function visibility keywords, iterators and the like, one can build a strong application that would be intrisically harder for a user to break. By storing script variables in a Vars collection class, as a member of the App class, and passing around a reference to the app as the first arg to all child objects, access to any part of the system is reasdily available without the need for a global.

    PHP Code:
    class Parent {
     
    private 
    $_child;
     
    public function &
    Child() {
     
    //dont create the object in __construct unless you have to
    // use just in time instancing if possible to reduce mem usage
    if (!is_set($this->_child)){ $this->_child = new Child($this); }
    return 
    $this->_child;
     
    }
     
    // add other functions
     
    }
     
    class 
    Child {
     
    private 
    $_parent;
     
    public function 
    __construct(&$parent) {
    $this->_parent $parent;
    }
     
    // add other functions

    This can go as deep as you want and no matter where you are in the code you have access to any other part, without having to worry about creating a new instance of things. It is very messy though...

    By deriving both Parent and Child from EventBroker, you could now add the "Cry" Event to Child and during the course of some other function, if the need arises, you can raise that event. If the Parent is listening for that event, it can now act accordingly.

    PHP Code:
    class Parent extends EventBroker {
     
    private 
    $_child;
     
    public function &
    Child() {
     
    //dont create the object in __construct unless you have to
    // use just in time instancing if possible to reduce mem usage
    if (!is_set($this->_child)){ 
    $this->_child = new Child();
    $this->_child->Register(&$this'Child_Cry''Cry'); }
    return 
    $this->_child;
     
    }
     
    public function 
    Child_Cry() { $args func_get_args(); // do something }
     
    }
     
    class 
    Child extends EventBroker {
     
    public function 
    __construct() {
    parent::__construct('Cry');
    }
     
    public Function 
    DepriveToy() {
    // do stuff
    parent::RaiseEvent('Cry', array('I want my toy!'));
    }
     
    public Function 
    DepriveFood() {
    // do stuff
    parent::RaiseEvent('Cry', array('I am Hungry!'));
    }
     

    The parent can now respond to the child based on the what it says while crying. The beauty of this is the child doesn't need to know anything about the listener. What the parent goes on vacation and needs a babysitter? The babysitter can now hook into child the same way as the parent did. It's a loosely coupled event-handler dispatching mechanism.

    McGruff,

    A transaction is a unit of application logic that can be performed on data, such inserting a record. An event is a way of telling other objects that something has just happened within a given class, perhaps a transaction, so they can perform ancillary actions. For example, in you project you define a class hierarchy similar to the below psuedo-code...

    App {
    $_users;
    $_groups;
    Users() {}
    Groups() {}
    }

    ...if a Group record is deleted, the app needs to then adjust all users that used that group in some fashion. While the act of deleting and updating records are transactions, the bridge that tell the app to update others records as a result of the one record being deleted is an event. They way we do this is to give the Groups class an event hat can be listened to by other objects, in this case the App object. When the app gets the event, based on the event name/data, it knows what to do. My broker object does just this. It gives classes, that derive from it, the ability to define and raise their own events, as well as respond to events belonging to other classes.

    Please note that my code was designed for use with PHP5 and may not be suitable for PHP4 as auto-dereferencing is not available and inhibits the building of class hierarchies.

    Certainly one could expand on my sample class, perhaps creating a base Event class, an Events collection class, a Handler class, a Handlers collection class, and then derive some specialized event types by extending from Event.

    Sorry for going on....I was bored.

  5. #5
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Still not clear about events. If they can be anything at all: presentation, model, coarse or fine, the term perhaps isn't very useful.

    I think, by making everything globally visible, you are sidestepping the design problem ie layering / getting objects to talk to each other. Objects should only be able to see just what they need to.

    PS: I was referring to Fowler's TransactionScript not to db transactions.

  6. #6
    SitePoint Evangelist
    Join Date
    Jun 2003
    Location
    Melbourne, Australia
    Posts
    440
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by McGruff
    Still not clear about events. If they can be anything at all: presentation, model, coarse or fine, the term perhaps isn't very useful.
    Seems to me that the problem here is "what is an event"? Is it something the user (browser issuing the request) does, or some change to an application object? McGruff considers the term best used to describe the former, whereas Serenarules uses it to describe the latter.
    Zealotry is contingent upon 100 posts and addiction 200?

  7. #7
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    Your assumption is correct. I come from over 12 years in VB and 2 in vb.net, therefore, I am prone to those idioms. What I consider an event occurs between instantiated classes as a means of notification, rather than to initiate a transaction. Perhaps 'trigger' would make a better term. Raising an event triggers a listening object to take it's own actions.

    Hope that's better.

  8. #8
    SitePoint Enthusiast
    Join Date
    Oct 2004
    Location
    Kansas City, MO
    Posts
    68
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Wouldn't the Observer pattern be the answer to your problem? See the article on phpPatterns.com for a great example of how it can be implemented.

    Just seems like you're re-creating the wheel here. If you use the Observer pattern it would be easier for folks to get what you're doing.

  9. #9
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    It was based on it in fact. There's a few differences however. My class not only allows multiple observers, as the one at phppatterns does, but it allows the observer to pick which events to monitor, which function is to be the callback (lessens dependancy on the existance of a particular function, the passing of varying numbers of arguments, the ability for the 'observable' object to define a valid list of observable events. It's a many to many relationship rather than one to many. Instead of supporting a single array of observers, it maintains an array of supported events, where each event maintains an array of observers.

    The whole point is to make the transition for M$ developers easier, consider the following faux vb code...

    Code:
    public class MyClass
     
    private withevents MyControl as clsMyControl;
     
    public sub New()
    MyControl = new clsMyControl
    end sub
     
    private sub MyControl_OnChange() Handles clsMyControl.OnChange
    'test the change and reset if needed
    end sub
     
    end class
     
    public class clsMyControl
     
    public event OnChange
    private Value as String
     
    public sub SetValue(byval Val as string)
    Value = Val
    RaiseEvent OnChange
    end sub
     
    end class

  10. #10
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    Sorry for the double, but I felt this needed a new post...

    It seems I need to better demonstrate the advantage of my class over the standard observable class. I came up with an example scenario where the standard implementation of an observable class would fall short. Here is the result. Hopefully this will make more sense.

    Thing class (functional code removed for brevity)
    PHP Code:
    class Thing extends EventBroker {
     
    public function 
    __construct() {
     
    // this object supports these three events
    parent::__construct('event1,event2,event3');
     
    }
     
    // place functions here and raise events thusly:
    // parent::RaiseEvent('event1' [, array $args]);
    // parent::RaiseEvent('event2' [, array $args]);
    // parent::RaiseEvent('event3' [, array $args]);
     

    Container class (functional code removed for brevity)
    PHP Code:
    class Container {
     
    private 
    $_thing1;
    private 
    $_thing2;
     
    public function 
    __construct() {
     
    // create objects
    $this->_thing1 = new Thing();
    $this->_thing2 = new Thing();
     
    // register hooks (callObject - callFunction - forEvent)
    $this->_thing1->Register($this'thing1_event1''event1'); // only monitor thing1.event1
    $this->_thing2->Register($this'thing2_event3''event3'); // only monitor thing2.event3
     
    }
     
    public function 
    thing1_event1() {
    // get the func args and do whatever
    }
     
    public function 
    thing2_event3() {
    // get the func args and do whatever
    }
     
    // add other custom functions here...
     

    As you can see, the ability to select which events to monitor and the ability to register a custom callback, results in code which is much cleaner, removes the depenancy on an expected accessor function, such as 'update()', and operates more like VB code. After looking at all the samples available once more, one major difference comes to mind. While the observer pattern seems designed to allow many objects to monitor a single object, with a single 'event', mine is designed to let a single container respond to many objects, using selected events and ignoring others. If used outside the scope of a container class, it can behave in much the same way as the standard observer.

  11. #11
    SitePoint Enthusiast
    Join Date
    Oct 2004
    Location
    Kansas City, MO
    Posts
    68
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok - now it's making a bit more sense. I didn't dive into the code fully, if I had I'm sure I would have seen that.

    One question for you: how will you handle ordering? Put another way, what happens to an event that needs to execute first or last?

    One thing I just noticed: EventBroker::Register()'s first parameter, $listener, doesn't need the "&" in PHP 5 - an object is already passed by reference.

    (shameless plug warning) You might want to check out how phpCR handles events. At it's most basic level, you register an EventListener and an EventSelector. The EventListener is notified when an Event goes off and EventSelector says it should be excepted. How EventSelector determines that is up to the implementor, but the idea keeps everything very compartmentalized - it's based on Java so that's what I'd expect

  12. #12
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    Currently ordering is as expected, fifo, but that is mainly because I haven't had a need to have it any other way in my own code. As object oriented as PHP has gotten, it's still basically linear in nature, in that scripts are executed from top to bottom (making small hops in between) and eventually exiting. With that in mind, I saw no reason to expect anything other than fifo, however, it's easily tweaked if need be.

    I know about the automatic dereferencing of objects in returns, but I was unaware that objects passed in to functions were treated the same. Are you certain this is the case? I would be surprised (quite alarmed actually) if function parameters were passed by default as anything other than by value, object or otherwise.

  13. #13
    SitePoint Addict
    Join Date
    Apr 2004
    Location
    Melbourne
    Posts
    362
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Objects are passed around by reference in PHP5, as in Java and C#.

    If you're used to the .NET method of event handling, the following is how I've implemented events without the need for superparent class.

    A simple event class.
    PHP Code:
    <?php
    /*
    * Entity for PHP5. A database object persistence framework
    * Copyright (c) 2003,2004, Marcus Nyeholt
    * All rights reserved.

    * Redistribution and use in source and binary forms, with or without 
    * modification, are permitted provided that the following conditions are 
    * met:

    * Redistributions of source code must retain the above copyright notice, 
    * this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright 
    * notice, this list of conditions and the following disclaimer in the 
    * documentation and/or other materials provided with the distribution.
    * Neither the name of Entity nor the names of its contributors may be 
    * used to endorse or promote products derived from this software without 
    * specific prior written permission.

    */

    /**
     * Event
     * An event chain collection. 
     * @package Entity
     * @author Marcus
     * @copyright Copyright (c) 2004
     * @version $Id: Event.php,v 1.1 2004/12/07 11:56:54 tanus Exp $
     * @access public
     **/
    class Event extends ArrayObject
    {
        
    /**
         * Event::Fire()
         * Fire all the event handlers for this event.
         * @return 
         **/
        
    public function fire(EventParameter $params)
        {
            foreach(
    $this as $eventHandler) {
                
    $eventHandler->handle();
            }
        }
        
        
        
    /**
        * Add an event handler to the chain
        *
        * @param EventHandler the handler of this event.
        */
        
    public function addHandler(EventHandler $handler)
        {
            
    $this[] = $handler;
        }
    }
    ?>
    This encapsulates the object handling the event along with the method used to handle it.
    PHP Code:
    <?php
    /*
    * Entity for PHP5. A database object persistence framework
    * Copyright (c) 2003,2004, Marcus Nyeholt
    * All rights reserved.

    * Redistribution and use in source and binary forms, with or without 
    * modification, are permitted provided that the following conditions are 
    * met:

    * Redistributions of source code must retain the above copyright notice, 
    * this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright 
    * notice, this list of conditions and the following disclaimer in the 
    * documentation and/or other materials provided with the distribution.
    * Neither the name of Entity nor the names of its contributors may be 
    * used to endorse or promote products derived from this software without 
    * specific prior written permission.

    */

    /**
     * EventHandler
     * This encapsulates the object handling the event along with the method used to handle it.
     * @package Entity
     * @author Marcus
     * @copyright Copyright (c) 2004
     * @version $Id: EventHandler.php,v 1.1 2004/12/07 11:56:54 tanus Exp $
     * @access public
     **/
    class EventHandler
    {
        
    /**
        * The name of the method that will handle the event.
        */
        
    protected $handler;
        
        
    /**
        * The object the handler will be called in.
        */
        
    protected $handlingObject;
        
        
    /**
         * EventHandler::__construct()
         * Create an event handler.
         * @param $handlerName The name of the method to call to handle an event.
         * @param $object The object to call that event in. If it's null, can just
         *                     call the method in a global context.
         * @return 
         **/
        
    public function __construct($handlerName$object=null)
        {
            
    $this->handler $handlerName;
            
    $this->handlingObject $object;
        }
        
        
    /**
         * EventHandler::Fire()
         * Actually causes the event to be handled.
         * @return 
         **/
        
    public function handle()
        {

            if( 
    $this->handlingObject == null ) {
                throw new 
    Exception("Null event handling object ");
            }
            
            if( 
    method_exists($this->handlingObject$this->handler) ) {
                
    $func $this->handler;
                
    $obj $this->handlingObject;

                
    $obj->$func();
            }
            else if( 
    function_exists($this->handler) ) {
                
    $func $this->handler;
                
    $func();
            }
        }
    }
    ?>
    This is a just a simple event parameter class, it can be overridden to have any sort of local members/methods.

    PHP Code:
    class EventParameter extends ArrayObject
    {
        

    Sample usage would be something like
    PHP Code:
    class dummy 
    {
        public 
    $displayPanelEvent;

        protected function 
    __construct()
        {
            
    $this->displayPanelEvent = new Event();
            
    $this->displayPanelEvent->addHandler(new EventHandler('setContentPanel'$this));
        }

        public function 
    setContentPanel($params)
        {
                     
    // react to the params 
        
    }
    }

    $class = new Dummy;
    $class->displayPanelEvent->fire(new CustomParameter('displayinfo')); 
    As in your implementation, it too allows for multiple handlers for an event, but adds the added flexibility of it being decentralised from a super class.

  14. #14
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    Believe it or not, I'm now confused...

    What is supposed to happen with $params in the following...

    PHP Code:
        public function fire(EventParameter $params
        { 
            foreach(
    $this as $eventHandler) { 
                
    $eventHandler->handle(); 
            } 
        } 
    ...it doesn't look like it's ever used.

  15. #15
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)


    Could be a side effect from refactoring maybe? Seen me being hit with that as well.

  16. #16
    SitePoint Addict
    Join Date
    Apr 2004
    Location
    Melbourne
    Posts
    362
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Serenarules
    Believe it or not, I'm now confused...

    What is supposed to happen with $params in the following...

    PHP Code:
        public function fire(EventParameter $params
        { 
            foreach(
    $this as $eventHandler) { 
                
    $eventHandler->handle(); 
            } 
        } 
    ...it doesn't look like it's ever used.
    Er, yeah, it's supposed to be passed through when the events are fired .

  17. #17
    SitePoint Enthusiast
    Join Date
    Oct 2004
    Location
    Kansas City, MO
    Posts
    68
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by Serenarules
    Currently ordering is as expected, fifo, but that is mainly because I haven't had a need to have it any other way in my own code. As object oriented as PHP has gotten, it's still basically linear in nature, in that scripts are executed from top to bottom (making small hops in between) and eventually exiting. With that in mind, I saw no reason to expect anything other than fifo, however, it's easily tweaked if need be.
    I've run into this with a plug-in system (event-driven) that I designed back over the summer. I need to control the ordering, because the plug-ins weren't necessarily going to be loaded in the order they would be exectued, but rather would be loaded in alphbetical order based on their filename.

    As part of the register() method, I had a "[, $order = null]" parameter. If it wasn't specified, it was just added as the next in line. If an order was specified, it was moved to that particular spot within the array and bumped anything else out of the way.

    The array looked like this:
    Code:
    $events = array('EventName' => array(0 => 'pluginName', 1 => 'plugin2Name'));
    Before an event's functions/methods were run, you just run a krsort() on $array['EventName'] and the highest value comes out first. That was designed to work in PHP4, a new PHP5 would would do better to implement an Iterator to store all of the events in and have that automatically determine where everything is at.

    Quote Originally Posted by Serenarules
    I know about the automatic dereferencing of objects in returns, but I was unaware that objects passed in to functions were treated the same. Are you certain this is the case? I would be surprised (quite alarmed actually) if function parameters were passed by default as anything other than by value, object or otherwise.
    As Tanus put it - objects are handed around by reference by default. That's one of the reason OO-code runs so much faster in PHP5 than in PHP4, there's not as many copies of the same object floating around in the memory. In order to make a copy of the original, all you need to do is call "$myNewInstance = clone $myInstance;".

  18. #18
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I'm still wondering exactly what an "event-driven" architecture is. What is it that distinguishes this from other designs? What are it's advantages? In a topic explaining how to use event driven classes, I'd like to know exactly what is meant by an "event".

    I think there is something fundamentally wrong with trying to join everything together with Observer but perhaps I just don't understand the event driven paradigm very well.

  19. #19
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    Ok, here's the basic premise, and I apologize if some consider it crude, butit's the best 'real life' example I can think of.

    EX: Joe farts. Then Sam scolds him and Max leaves the room.

    In the example above Joe performed a transaction of some sort and a result was produced, the fart. After the fart was produced Joe is done with it, he doesn't care what else happens. Now, the other people in the room (observers) notice the fart via the sense of smell (an event - smelling the odor). Once the event (or trigger if you will) was detected, Sam and Max were free to take thier own actions, independant of each other and the event producer.

    Granted, this type of architechture is more useful in persistant applications, such as desktop programs, however, there are reasons to use it even in a basically linear language.

    Let's say we develop the following objects: Recordset, Record, Fields, Field

    We want to design the hierarchy so that internal communication is easily done without exposing too many public properties or methods. Let's assume the Record class implements a Delete function which removes the record from the database. The Recordset object needs to be told to remove the object from the array now as well. To do that we post an event (or simple notification) and all observers (in this case the containing Recordset object), and it can respond. What this does is keep communications internal to the hierarchy without having to expose a public Function in the Recordset object to perform the element removal and means that the Record object does not have to directly maintain a reference to it's parent.

    [faux code]
    Recordset { // listener in this case
    private Add()
    public Count()
    public Item()
    private Remove()
    private Handle_OnDelete() // validates event and calls Remove
    }

    Record {
    public Fields()
    public Delete() // uses callback to post an event "OnDelete" passing ID to listener
    ...
    }

    The reason we have this is because a Record object can be created outside the hierarchy with default settings and then passed into Recordset.Add(). It can also be created using the id in the constructor to autoget a single record. Therefore the actual database level insert, update and delete calls are made with Record, while collection level Add and Remove is handled at the Records level. THe event system allows the two to interact without revealing too much to the public on how it works. This lessens the risk that a novice developer could missuse your API.

    If anybody else can state it clearer, or provide better examples, please do so.

  20. #20
    Non-Member
    Join Date
    Jan 2003
    Posts
    5,748
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    the event producer.
    The farter in this case

  21. #21
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Excuse me for playing devil's advocate but I'm trying to see the big picture here. Let me show you where I've got to so far.

    In an event-driven architecture, the request event might cascade down through the app triggering various secondary events & etc. Fundamentally, all events arise from http requests. Event listeners (may) perform actions of some kind: the sum of these actions is the response to the original http request.

    In the sense that the application performs a series of actions in response to an http request, an event-driven design is no different to any other architecture. What it's all about is using Observer to hook objects up together.

    There are all kinds of patterns with which to aggregate objects. Observer is useful if you want to pass the same "event" to multiple observers (event listeners). The observable does know about the listeners, and it does have to know a method call, but it doesn't know exactly what it is they do and it doesn't care how many there are. This is similar to a Chain of Responsibility without the "switch-case" logic: independent observer objects can be attached and detached at will.

    I think I can see why this could be a valid design choice for a controller which decomposes into nested sub-controllers a la WACT and that maybe is one answer to the question "what is an event?": specifically, it's something going on in the controller-layer which other sub-controllers need to know about.

    However, an observable essentially "broadcasts" events rather than targetting specific objects. I suspect that, if you find yourself narrowing the target with named objects, named method calls, and variable parameter lists that could be a bad smell which might suggest a different approach to get the objects in question talking to each other.

  22. #22
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    Responses in quote...

    Quote Originally Posted by McGruff
    Excuse me for playing devil's advocate but I'm trying to see the big picture here. Let me show you where I've got to so far.

    In an event-driven architecture, the request event might cascade down through the app triggering various secondary events & etc. Fundamentally, all events arise from http requests. Event listeners (may) perform actions of some kind: the sum of these actions is the response to the original http request.

    Ok, what were discussing is purely serverside coding and has nothing to do with clientside interaction, as is the case with the ASP.NET methodology.

    In the sense that the application performs a series of actions in response to an http request, an event-driven design is no different to any other architecture. What it's all about is using Observer to hook objects up together.

    Right, only we're extending the concept to make it better suited to a many to many relationship between objects without those objects having to support an over abundance of public methods and properties.

    There are all kinds of patterns with which to aggregate objects. Observer is useful if you want to pass the same "event" to multiple observers (event listeners). The observable does know about the listeners, and it does have to know a method call, but it doesn't know exactly what it is they do and it doesn't care how many there are. This is similar to a Chain of Responsibility without the "switch-case" logic: independent observer objects can be attached and detached at will.

    Right, and again, the basic Observer pattern is a many to one relationship, where many observers can listen to the same object. Even within this scope a single call to update() is issued. This isn't very flexible. What we've done here in our various implementations to make it not only a many to many relationship, but each observed object can now post multiple events. Using the handlers the observers register for each event, we clean up the code as there are no longer any master switch callbacks.

    I think I can see why this could be a valid design choice for a controller which decomposes into nested sub-controllers a la WACT and that maybe is one answer to the question "what is an event?": specifically, it's something going on in the controller-layer which other sub-controllers need to know about.

    I stated in an earlier post my basic definitions as being:
    transaction: the process of acting upon data
    event: a notice to external objects that a transaction has just been processed
    :don't know if you missed that one or not.

    However, an observable essentially "broadcasts" events rather than targetting specific objects. I suspect that, if you find yourself narrowing the target with named objects, named method calls, and variable parameter lists that could be a bad smell which might suggest a different approach to get the objects in question talking to each other.

    Of course, our systems basically provide Event-Handler mappings. The Observable still only fires the event and the Observer still only hears it. The usage of parameters is safe in this context as they are passed as a single array. It can be safely ignored in handlers that do not require it. That aside, I'm working on a more expanded version of this concept now that involves a solid set of classes and interfaces for constructing custom Event types so that a class can instantiate and fire things such as new OnTxBegin() and OnTxComplete, etc... making the whole thing much more implementable.

  23. #23
    SitePoint Enthusiast
    Join Date
    Oct 2004
    Location
    Kansas City, MO
    Posts
    68
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by McGruff
    Excuse me for playing devil's advocate but I'm trying to see the big picture here. Let me show you where I've got to so far.

    ... snip ...

    However, an observable essentially "broadcasts" events rather than targeting specific objects. I suspect that, if you find yourself narrowing the target with named objects, named method calls, and variable parameter lists that could be a bad smell which might suggest a different approach to get the objects in question talking to each other.
    I think you're dead on here - you're moving away from the Observer when you start getting specific. When an Observable starts determining what should go where, it's moved away from it's main purpose and started multi-tasking, as it were.

    My thought is that the Observable should just know that Observer::update(Observable $o) exists, then it's up to $o to determine if it should execute based on the state of $o (in this case, $o::getState()).

    Now, if you move to an ObservationManager where you have a Singleton handling all events, it's a different story. It probably should filter out requests prior to handling them off to the Observer. At this point you're implementing a pseudo-Observer pattern though. The whole dynamic of the code changes though:

    Code:
    // Under the traditional Observer
    $this->setState('updated');
    parent::notifyObservers();
    // calls update($this) on and lets them sort it out for themselves
    
    // Under the ObservationManager
    $this->setState('updated');
    ObservationManager::notifyObservers($this);
    // ObservationManager determines who should be notified
    I'm not sure one is better than the other necessarily, the latter example does require ObservationManager know everything that's happening.

    Quote Originally Posted by Serenarules
    Of course, our systems basically provide Event-Handler mappings. The Observable still only fires the event and the Observer still only hears it. The usage of parameters is safe in this context as they are passed as a single array. It can be safely ignored in handlers that do not require it. That aside, I'm working on a more expanded version of this concept now that involves a solid set of classes and interfaces for constructing custom Event types so that a class can instantiate and fire things such as new OnTxBegin() and OnTxComplete, etc... making the whole thing much more implementable.
    Check out this source and these files for some ideas. That might even do what you're looking for.

  24. #24
    Resident OCD goofball! bronze trophy Serenarules's Avatar
    Join Date
    Dec 2002
    Posts
    1,911
    Mentioned
    26 Post(s)
    Tagged
    0 Thread(s)
    Travis, that's very close to what I'm working on actually. I looked at several of the files and there are a great many similarities. I'll post links to my own code once completed and we can discuss the validity of my code at that time. I value professional comments.

  25. #25
    ********* Victim lastcraft's Avatar
    Join Date
    Apr 2003
    Location
    London
    Posts
    2,423
    Mentioned
    2 Post(s)
    Tagged
    0 Thread(s)
    Hi...

    Quote Originally Posted by Serenarules
    I value professional comments.
    Oh well, you probably won't like these then . Your original links are not working, so I haven't read your orginial code. These coments are also a bit random.

    I think the pattern you have implemented is called SignalsSlots. In this case you have used strings rather than with the type system of C++. I am not too clear on what exactly SignalsSlots is though, because the original C implementations got a bit mangled when Trolltech reproduced it for KDE...

    http://doc.trolltech.com/3.0/signalsandslots.html

    The Trolltech version has every component act as a routing mechanism for everything in it's collection, rather than some central table. I suspect the original C implementations are closer to your version judging by your replies to McGruff.

    Despite all of this I have to say that I remain suspicious of it's usage in PHP. With user interaction it is common to have all sorts of components broadcast information in real time. The logic of such requests is usually simple and the error handling non-existent. Also there are usually a very large number of small components. With a typical PHP script there is no real time interaction, it's a batch job sequence, and the hit and run nature of the scripts means that we deal with a few objects once and pay the price of construction every time.

    Also I don't fancy debugging all of this. What happens when an observer emits an event in response to a signal. How do you prevent loops?

    The problem with using it on the domain side is that it means your business logic is distributed into event emitters and handlers with a non-obvious connection between the two. The most expensive problems to put right are the ones where your program departs from it's intended use because either one wasn't understood properly. We already have a message passing system (->), but in this one the connections are explicit and we can respond to errors in a transaction safe way.

    Whenever you rewrite the semantics of the language (e.g. the Command pattern) you pay a big price in complexity. Other developers will find it much harder to figure out what's happening (they often have to actually run the code) and you will hack lumps out of encapsulation and will have to come up with systems to replace it. That's a lot of documentation.

    This event driven set up seems to me to be better as a techy solution to a techy only problem. Composite templates handling requests (a'la WACT) are a good one here. The structure is arbitrary (a custom tag driven template) and the incoming parameters need to be filtered and selectively broadcast. I am having trouble thinking up other juicy examples.

    You mention persistent data structure updates. I am not sure this is an appropriate usage. After all you have comparitively few signals; create, retrieve, update and destroy. There isn't much need to do these in real time, in fact you could generate excess queries this way. Also a ResultSet would know about it's children anyway when and as they were added. A delete message is the only one that causes problems to the ResultSet (counting replace as a delete and then a create). A probably simpler approach is not to allow delete() to be called on a Result. Either the same class that does the find() has a delete() or you only allow it to be called on the ResultSet().

    One possible usage I can think of is complex rules, say the filtering in some kind of support system. As the filtering system parses the incoming customer mail, it triggers events amongst the rules collection (again arbitrary and frequently changing) that results in a scores that will assign it to somebody as a support ticket.

    Interesting. Did you have a specific compelling usage in mind when you wrote this system?

    yours, Marcus
    Marcus Baker
    Testing: SimpleTest, Cgreen, Fakemail
    Other: Phemto dependency injector
    Books: PHP in Action, 97 things


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •