event driven: how to specify the contract?
Moderator: General Moderators
event driven: how to specify the contract?
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?
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?
'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?
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.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 } } }
Re: event driven: how to specify the contract?
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?
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.
"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?
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?
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.
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?
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.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.
It's only skeleton code above.
Re: event driven: how to specify the contract?
Possible improvements:
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).
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; }
}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?
// off topic
I remember Jenk loves final modifiers

I remember Jenk loves final modifiers
There are 10 types of people in this world, those who understand binary and those who don't
Re: event driven: how to specify the contract?
Ha.. I was about to post about that 
Re: event driven: how to specify the contract?
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 meanJenk 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 } } }
Code: Select all
$form->getElement( 'person' )->populateTo( $user->getPerson() );
Code: Select all
$form->getElement( 'person' )->populateTo( $user );
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?
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.josh wrote:Jenk wrote: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?Code: Select all
$form->getElement( 'person' )->populateTo( $user );
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?
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..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.
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?
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.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.
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.