动态路由器和控制消息
我正在开发的系统使用类似动态路由器+ dep.injection 模式的东西,具体来说:
- 路由器对象使用配置来实例化模块对象。模块是独立的并且可能有很多。
- 路由器对象的实例被依赖注入到模块构造函数中,并用作 API 引用/指针(可能与问题无关,但这里是为了清楚起见)
- 模块使用 API 添加将输入模式链接到特定模块方法的规则
- 路由器接收输入,根据规则集检查它并调用匹配的模块方法
- 结果由路由器收集并传递给输出处理器
模块构造器:
class module {
public function __construct(&$router) {
$router->addRoute('some-input-pattern', array($this, 'someMethod'));
}
public function someMethod() {
return 'some arbitrary result';
}
}
无论这看起来多么简单,在某些情况下模块和路由器需要就某些不相关的内容进行通信与任意返回值相关。
例如,有时模块必须触发事件或异常,而这些事件或异常必须由模块本身处理。它可以源自任何模块方法,并且必须由特定的方法处理。 (在每个方法中都有大量的 try/catch 块似乎并不正确,并且可以处理任何异常的唯一“入口点”位于模块外部,并且在路由器中。模块抛出,模块句柄,但只有路由器可以听起来是错误的。)
class module {
public function __construct(&$router) {
...
$router->addExceptionHandler('dbTableNotFoundException',
array($this, 'installSchema'));
}
}
在其他情况下,有些事件需要由路由器本身处理。例如,模块可以请求路由器继续处理,就好像它收到了一种特殊类型的输入一样,忽略真实的输入。然后,路由器必须根据规则集重新检查它,并调用适当的方法。
public function someMethod(){
...
throw routerRestartException('special-input');
}
在其他情况下,除了将事件传递到输出之外,事件可能不需要任何处理,例如仅人眼看到的错误。
public function someMethod(){
...
throw humanEyesException("You can't do that!");
}
这有效地引导我思考两个通信方面,因为路由器和模块交换任意返回数据和特殊控制消息。
到目前为止,系统控制消息方面是通过模块抛出异常和路由器捕获来实现的。这要求模块注册两种类型的方法——一种用于处理输入,另一种用于处理异常,这可能很臭。
一些异常似乎会影响流量控制,我相信为此使用异常也不是一个好的做法。
问题是,是否有最佳实践或模式来在路由器对象及其依赖模块之间构建这种两方面的通信?
编辑
一路上的一个想法:通过将模块内异常处理与路由器分离来改善情况的一种方法可以是使用自定义异常的观察者模式:
public function __construct(&$router){
...
dbTableNotFoundException::addObserver(array($this, 'installSchema'));
}
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 communication 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'));
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
为了让模块对其他模块和 API 事件做出反应,我将使用观察者模式。您的 API 可以提供多个可观察主题(这些可以是类,API 可以为每个可观察主题提供取消/注册和通知过程)
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)