Page 1 of 3

event driven: how to specify the contract?

Posted: Tue Jul 28, 2009 3:55 pm
by koen.h
I like the idea of having part of my code driven by events. My idea of that is to have an event dispatcher where objects can register themselves for certain events. This would be like an observer pattern.

I can think of two problems with this:
1) the order of event handlers: Some event handlers may need to be called before others so you would have to register them first. This isn't very dynamic.
2) the contract between handlers and callers: When an object updates the event dispatcher with an event (eg auth_user), he has to provide some arguments (the object itself, or user_id and password, or someting else). These are not in a contract like the interface provides one.

The question is actually more about the second problem. How could I ensure a contract so the client code (observer, handler) knows what arguments he can expect?

Re: event driven: how to specify the contract?

Posted: Wed Jul 29, 2009 4:08 am
by Jenk
'He' can't. Your observers will need to validate.

Code: Select all

public function notify($msg, $args) {
  if ($msg == $this->message) {
    if ($this->validateArgs($args)) {
      // do stuff
    }
  }
}

Re: event driven: how to specify the contract?

Posted: Wed Jul 29, 2009 5:55 am
by koen.h
Jenk wrote:'He' can't. Your observers will need to validate.

Code: Select all

public function notify($msg, $args) {
  if ($msg == $this->message) {
    if ($this->validateArgs($args)) {
      // do stuff
    }
  }
}
From the other thread about type hinting I can see why you don't have a problem with this. Since I'm on the other side you probably can see too why I do have a problem with this.

Re: event driven: how to specify the contract?

Posted: Wed Jul 29, 2009 6:49 am
by Jenk
Even with type hinting, you won't be able to implement this pattern without each observer checking.

Re: event driven: how to specify the contract?

Posted: Fri Jul 31, 2009 7:50 am
by koen.h
An option can be the following (from Design Patterns):

"To address the parameter-passing problem, we can use separate request objects that bundle request parameters."

It's in the chapter about chain of responsability. One implementation of that pattern faces the same problem and this is their solution.
Ported to my problem, an event could be an event object.

Code: Select all

class EventHandler {
    public function register($event, Observer $observer) {}
    public function notify(Event $event) {}
}
 
abstract class Observer {
    public function update(Event $event) {
        $method = $event->getName();
        try { $this->$method($event); }
        catch (Exception $e) {}
    }
}
 
interface Event {
    public function getName();
}
 
class OnAuthEvent implements Event {
    private $name = 'onAuth';
    public function __construct($username, $password) {}
    public function getName() {}
    public function getUsername() {}
    // etc
}
 
class Auth {
    public function __construct($eventHandler) {}
    public function auth() {
        $this->eventHandler->notify(new OnAuthEvent($username, $pass));
    }
}

Re: event driven: how to specify the contract?

Posted: Fri Jul 31, 2009 7:55 am
by koen.h
At first sight I'm happy with this. If the first problem can be solved too I'm going in the direction of having the entire flow of the program dynamically rewirable.

Re: event driven: how to specify the contract?

Posted: Fri Jul 31, 2009 8:02 am
by Jenk
What happens if your event needs to throw an Exception? Nothing.. as your catch block does nothing and the exception (and problem) will go ignored.

In terms of functionality you've done nothing different to what I posted, except introduced a big potential bug. Bad.

Re: event driven: how to specify the contract?

Posted: Fri Jul 31, 2009 8:21 am
by koen.h
Jenk wrote:What happens if your event needs to throw an Exception? Nothing.. as your catch block does nothing and the exception (and problem) will go ignored.

In terms of functionality you've done nothing different to what I posted, except introduced a big potential bug. Bad.
These {} are put there for syntaxis only. There should be code in there. When I give example code here I tend to write {} instead of something like { // code goes here }. Maybe I should start writing the latter.
It's only skeleton code above.

Re: event driven: how to specify the contract?

Posted: Fri Jul 31, 2009 8:55 am
by koen.h
Possible improvements:

Code: Select all

abstract class Event {
    private $name;
    private $handled = false;
    public function getName() { return $this->name; }
    public function isHandled() { return $this->handled; }
}
 
abstract class ActionEvent implements Event {
    public function setHandled() { $this->handled = true; }
}
 
abstract class FilterEvent implements Event {
    final public function isHandled() { return false; }
}
And from:
http://www.javaworld.com/javaworld/jw-0 ... tml?page=4

Maybe it's possible to implement a very coarse order configuration option by having methods like setNext(), or setLast(); Objects then can re-add themselves in the chain (eg a 404 redirector).

Re: event driven: how to specify the contract?

Posted: Fri Jul 31, 2009 9:09 am
by VladSun
// off topic
I remember Jenk loves final modifiers :lol: :twisted: :twisted:

Re: event driven: how to specify the contract?

Posted: Fri Jul 31, 2009 9:25 am
by Jenk
Ha.. I was about to post about that :lol:

Re: event driven: how to specify the contract?

Posted: Thu Aug 06, 2009 5:08 pm
by josh
Jenk wrote:'He' can't. Your observers will need to validate.

Code: Select all

public function notify($msg, $args) {
  if ($msg == $this->message) {
    if ($this->validateArgs($args)) {
      // do stuff
    }
  }
}
The problem I had with this was when mapping data from models to form elements and visa versa, I wanted to pass in only the model to be replaced. let me show you what I mean

Code: Select all

 
$form->getElement( 'person' )->populateTo( $user->getPerson() );
 
Here the element could call each one of the person's setters.... but lets say you wanted to _replace_ the person, ( new data for immutable setters ), youd have to do

Code: Select all

 
$form->getElement( 'person' )->populateTo( $user );
 
and have the element call setPerson() with the new object. Doesn't really violate the law of demeter IMO, unless you see anyway around this?

On the other hand if I had a collection then I pass in the collection itself, the element can call $collection->empty() and then add back the models it wants, can't do that with a single object who needs to be replaced with another object of another identity

Re: event driven: how to specify the contract?

Posted: Fri Aug 07, 2009 1:45 am
by koen.h
josh wrote:
Jenk wrote:

Code: Select all

 
$form->getElement( 'person' )->populateTo( $user );
 
and have the element call setPerson() with the new object. Doesn't really violate the law of demeter IMO, unless you see anyway around this?
Maybe not Demeter but I don't think it's good design. The place where you would have this code will need to be passed the $persone object, only to pass it to the form element. Ofcourse this may be not true as I can't see this context but these constructs are common.
I think a solution goes in the direction of another thread, about templates and avoiding getters/setters. Ideally you would be able to do something like $user->addTo($form); or so. I'm still trying to figure this out.

Re: event driven: how to specify the contract?

Posted: Fri Aug 07, 2009 12:28 pm
by josh
koen.h wrote: Ideally you would be able to do something like $user->addTo($form); or so. I'm still trying to figure this out.
What if you had superUserA and superUserB that had different forms, that both populate from users. If your form : model relationship is 1:1 that would work but in my case it was not. I don't see how its bad design either, a person has a user, an address, etc..

a person form has an address sub form, user sub form, etc...

I ask the person form to populate from a person object, I dont have to worry about what the person form is doin behind the scenes. My person form would populateFrom() a $person object, elements derive the setter / getter to call based on the name of the element, so if I add an address sub-form but name it permanent_address it populates itself from the Address object returned by calling ->getPermanentAddress() on the $person object is is passed from the person form.

Re: event driven: how to specify the contract?

Posted: Sat Aug 08, 2009 6:44 pm
by koen.h
josh wrote:What if you had superUserA and superUserB that had different forms, that both populate from users. If your form : model relationship is 1:1 that would work but in my case it was not. I don't see how its bad design either, a person has a user, an address, etc..

a person form has an address sub form, user sub form, etc...

I ask the person form to populate from a person object, I dont have to worry about what the person form is doin behind the scenes. My person form would populateFrom() a $person object, elements derive the setter / getter to call based on the name of the element, so if I add an address sub-form but name it permanent_address it populates itself from the Address object returned by calling ->getPermanentAddress() on the $person object is is passed from the person form.
The form here has to know about the internal state of the User (A, B). I think the responsibility of a form is to display data. Not to know what data to get or where to get it. SuperUserA should know what data it contains and add that to the form. Dito for SuperUserB.

I have to admit that what I'm saying here convinces me on a theoretical level. But I haven't had the chance to put it to practice and have always done the same as you. I only very recently got convinced I've did this wrong and need to put together some working examples.