Dynamic router and control messages
The system I'm working on is using something like a dynamic router + dep.injection pattern, specifically:
- A router object uses config to instantiate module objects. Modules are independent and may be many.
- An instance of the router object is dependency-injected into module constructor and is used as an API reference/pointer (probably irrelevant for the question, but is here for clarity sakes)
- modules use API to add rules that link input patterns to specific module methods
- Router receives input, checks it against the rule set and calls module methods that match
- Results are collected by the router and passed over to output processor
Module contructor:
class module {
public function __construct(&$router) {
$router->addRoute('some-input-pattern', array($this, 'someMethod'));
}
public function someMethod() {
return 'some arbitrary result';
}
}
However simple this may seem, there are cases when module and router need to communicate about something not related to arbitrary return values.
For instance, there are occasions when a module has to trigger an event, or an exception, that would have to be handled by the module itself. It can originate in any module method, and has to be handled by a specific one. (Having tons of try/catch blocks in every method doesn't seem right, and the only 'entry point' that can handle any exception is outside of the module, and in the router. Module throws, module handles, but only router can actually catch. Sounds wrong.)
class module {
public function __construct(&$router) {
...
$router->addExceptionHandler('dbTableNotFoundException',
array($this, 'installSchema'));
}
}
On other occasions there are events that need to be handled by the router itself. For example, module can request that the router proceeds as if it has received a special type of input, ignoring the real one. The router then has to recheck it against the rule set, and call appropriate methods.
public function someMethod(){
...
throw routerRestartException('special-input');
}
On other occasions events may require no handling other than passing them through to output, such as human-eyes-only errors.
public function someMethod(){
...
throw humanEyesException("You can't do that!");
}
This effectively leads me to thinking of two communication aspects, because router and modules exchange both arbitrary return data and special control messages.
So far the system control messages aspect is implemented by modules throwing exceptions, and the router catching. This requires that modules register two types of methods — one for processing input, and another for handling exceptions, which is probably smelly.
Some of the exceptions seem to affect flow control, and I believe using exceptions for that isn't a good practice either.
The question stands, is there a best practice or a pattern for building this kind of two-aspect communicat开发者_开发技巧ion between router object and its dependent modules?
EDIT
A thought along the way: one way of improving the situation by detaching in-module exception handling from the router could be using Observer pattern for custom Exceptions:
public function __construct(&$router){
...
dbTableNotFoundException::addObserver(array($this, 'installSchema'));
}
For letting modules react upon other modules and API events I would use the Observer pattern. Your API can provide several observable subjects (those can be classes be the API could provide un/register and notify procedures for each observable subjects)
精彩评论