读书人

前端控制器形式的介绍第2部分

发布时间: 2012-12-16 12:02:32 作者: rapoo

前端控制器模式的介绍,第2部分
接上一篇》》前端控制器中的应用,其关注的主要领域是调度命令,无论是静态或动态的,预定义的处理程序,如页面控制器,REST资源,或几乎任何其他想到像集中代理。建立至少一个天真的前端控制器是在相当教学经验,了解他们的基本事实,以促进这个想法,从务实的角度来看,我经历了一个做作的前端控制器的实施在介绍性的文章,打包所有的逻辑需要一个类的边界内的路由和调度请求。有关前端控制器的最好的事情之一是,你可以保持它们结构严密,运行路由和调度传入的请求,或者你可以让你野性的一面展示和实施一个完整的RESTful的控制器能够解析HTTP动词,可容纳预/调度后挂钩,像一个统一的API后面。不过,虽然这种做法是有吸引力,它打破了单一职责原则(SRP),和对面向对象本身不同的任务组成的代表团,积极推动几个细粒度的对象的性质。所以,这意味着我只是一个罪恶的灵魂,谁也不敢打破SRP的诫命呢?那么,在一定意义上我。所以我想洗去我的罪,你是多么容易部署小,但可扩展性,HTTP框架能够把工作伴随着一个独立的路由器和调度的一个前端控制器。另外,整个请求/响应周期将独立处理由两个可重用类,自然你就可以在将调整。有了这样一个庞大的HTTP框架与全功能组件包装的扩散,似乎荒唐从头前端控制器实现路由和调度请求,通过几个模块化类的,即使这些保留SRP的本质。在一个不起眼的尝试,以避免被判断为重新发明轮子,我的一些自定义实现块会得到启发的由拉尔斯Strojny书面的俏皮EPHPMVC库。
剖析请求/路由/调度/响应周期
我们要解决的首要任务是负责一个典型的HTTP请求/响应周期的数据和行为建模的类定义的情侣。这里是第一位的,再加上它实现的接口:

01<?php02namespace Acme\Library;0304interface RequestInterface05{06 public function getUri();07 public function setParam($key, $value);08 public function getParam($key);09 public function getParams();10}01<?php02namespace Acme\Library;0304class Request implements RequestInterface05{06 protected $uri;07 protected $params = array();08 09 public function __construct($uri, array $params = array()) {10 if (!filter_var($uri, FILTER_VALIDATE_URL)) {11 throw new \InvalidArgumentException("The URI is invalid.");12 }13 $this->uri = $uri;14 $this->params = $params;15 }16 17 public function getUri() {18 return $this->uri;19 }20 21 public function setParam($key, $value) {22 $this->params[$key] = $value;23 return $this;24 }25 26 public function getParam($key) {27 if (!isset($this->params[$key])) {28 throw new \InvalidArgumentException(29 "The request parameter with key '$key' is invalid.");30 }31 return $this->params[$key];32 }33 34 public function getParams() {35 return $this->params;36 }37}

请求类封装一起极其骨骼的HTTP请求的参数和模型的数组传入的URI。为简洁起见,额外的数据,如相关问题的请求的方法集的成员已被故意留下的图片之外。如果你觉得心情拖放到类,继续这样做。有一个苗条的HTTP请求包装自己的快乐生活,是一切都很好,罚款肯定的,但最终无用的,如果不加上对口模仿一个典型的HTTP响应的数据和行为。让的修复和建立互补的组件:

01<?php02namespace Acme\Library;03//www.heatpress123.net04interface ResponseInterface05{06 public function getVersion();07 public function addHeader($header);08 public function addHeaders(array $headers);09 public function getHeaders();10 public function send();11}01<?php02namespace Acme\Library;0304class Response implements ResponseInterface05{06 const VERSION_11 = "HTTP/1.1";07 const VERSION_10 = "HTTP/1.0";08 09 protected $version;10 protected $headers = array();11 12 public function __construct($version = self::VERSION_11) {13 $this->version = $version;14 }15 16 public function getVersion() {17 return $this->version;18 }19 20 public function addHeader($header) {21 $this->headers[] = $header;22 return $this;23 }24 25 public function addHeaders(array $headers) {26 foreach ($headers as $header) {27 $this->addHeader($header);28 }29 return $this;30 }31 32 public function getHeaders() {33 return $this->headers;34 }35 36 public function send() {37 if (!headers_sent()) {38 foreach($this->headers as $header) {39 header("$this->version $header", true);40 }41 }42 }43}

响应类无疑是比它的合作伙伴更加积极的生物请求。它就像一个基本的容器,它允许你随意堆起来的HTTP标头,并能够将它们发送到客户端太。有了这些做自己的事情,在隔离的类,它的时间,以解决在建设一个前端控制器的下一部分。在一个典型的实现,路由/调度过程中的大部分时间内封装相同的方法,其中老实说并不坏,在所有。然而,在这种情况下,它会是不错的打破问题的进程,并委派他们到不同的类。这样一来,事情是平衡的多一点,在各自的职责同样的。这里的一批得到路由模块的启动和运行的类:

1 <?php2 namespace Acme\Library;3 4 interface RouteInterface5 {6 public function match(RequestInterface $request);7 public function createController();8 }01<?php02namespace Acme\Library;0304class Route implements RouteInterface05{06 protected $path;07 protected $controllerClass;0809 public function __construct($path, $controllerClass) {10 if (!is_string($path) || empty($path)) {11 throw new \InvalidArgumentException("The path is invalid.");12 }13 if (!class_exists($controllerClass)) {14 throw new \InvalidArgumentException("The controller class is invalid.");15 }16 $this->path = $path;17 $this->controllerClass = $controllerClass;18 }19 20 public function match(RequestInterface $request) {21 return $this->path === $request->getUri();22 }23 24 public function createController() {25 return new $this->controllerClass;26 }27}01<?php02namespace Acme\Library;0304interface RouterInterface05{06 public function addRoute(RouteInterface $route);07 public function addRoutes(array $routes);08 public function getRoutes();09 public function route(RequestInterface $request, ResponseInterface $response);10}01<?php02namespace Acme\Library;0304class Router implements RouterInterface05{06 protected $routes = array();07 08 public function __construct(array $routes = array()) {09 if (!empty($routes)) {10 $this->addRoutes($routes);11 }12 }13 14 public function addRoute(RouteInterface $route) {15 $this->routes[] = $route;16 return $this;17 }18 19 public function addRoutes(array $routes) {20 foreach ($routes as $route) {21 $this->addRoute($route);22 }23 return $this;24 }25 26 public function getRoutes() {27 return $this->routes;28 }29 30 public function route(RequestInterface $request, ResponseInterface $response) {31 foreach ($this->routes as $route) {32 if ($route->match($request)) {33 return $route;34 }35 }36 $response->addHeader("404 Page Not Found")->send();37 throw new \OutOfRangeException("No route matched the given URI.");38 }39}

正如人们所预料的,还有大量的选项,选择当谈到执行功能的路由机制。以上,至少在我看来,公开务实和简单的解决办法。它定义了一个独立的路由类,将一个路径到一个给定的动作控制器,和一个简单的路由器,其责任是有限的检查,如果一个存储路由匹配的URI关联到一个特定的请求对象。要得到的东西终于整理出来,我们需要设立一个迅速调度,可以投入到工作与以往类并排。这正是下面的类:

1 <?php2 namespace Acme\Library;3 4 interface DispatcherInterface5 {6 public function dispatch(RouteInterface $route, RequestInterface $request, ResponseInterface $response);7 }01<?php02namespace Acme\Library;0304class Dispatcher implements DispatcherInterface05{06 public function dispatch(RouteInterface $route, RequestInterface $request, ResponseInterface $response) {07 $controller = $route->createController();08 $controller->execute($request, $response);09 }10}

扫描分派,你会发现两件事情有关。首先,它不进行任何国家。其次,它含蓄地假定每个动作控制器将运行在一个表面的execute()方法。这可以是一个稍微灵活的架构,有利于重构,如果你愿意(在我脑海中的第一件事是调整的实施路线类),但为简单起见,我会继续调度不变。现在你可能不知道如何以及在哪里能够一起把所有的上一类下降前端控制器。不要着急,因为这是未来!
实现一个定制的前端控制器
我们到达的那一刻,我们都一直在等待,从一开始就实施期待已久的前端控制器。但是,如果您所期望的实施要漂亮得多一些史诗级任务,班级数目,我们下降了,恐怕你会失望的。事实上,创建控制器归结为只是定义一个类,屏蔽路由器和一个极其简单的API背后的调度功能:
1 <?php2 namespace Acme\Library;3 4 interface FrontControllerInterface5 {6 public function run(RequestInterface $request, ResponseInterface $response);7 }01<?php02namespace Acme\Library;0304class FrontController implements FrontControllerInterface05{06 protected $router;07 protected $dispatcher;0809 public function __construct(RouterInterface $router, DispatcherInterface $dispatcher) {10 $this->router = $router;11 $this->dispatcher = $dispatcher;12 }13 14 public function run(RequestInterface $request, ResponseInterface $response) {15 $route = $this->router->route($request, $response);16 $this->dispatcher->dispatch($route, $request, $response);17 }18}

FrontController类是采用它的run()方法及其合作者通过幕后的功能路由和调度给定请求到相应的动作控制器。如果你想,该方法可能很多胖,封装了一堆额外的实施,如前/后派遣挂钩等等,。我离开你的功课,如果你想添加一个新的开发带缺口。看到如果设立的前端控制器,因为它似乎实际上是功能,让我们创建一个平庸的动作控制器,它实现一个情侣的execute()方法:

1 <?php2 namespace Acme\Library\Controller;3 use Acme\Library\RequestInterface,4 Acme\Library\ResponseInterface;5 6 interface ActionControllerInterface7 {8 public function execute(RequestInterface $request, ResponseInterface $response);9 }01<?php02namespace Acme\Library\Controller;03use Acme\Library\RequestInterface,04 Acme\Library\ResponseInterface;0506class TestController implements ActionControllerInterface07{08 public function execute(RequestInterface $request, ResponseInterface $response) {09 echo "Test controller called!";10 }11}01<?php02namespace Acme\Library\Controller;03use Acme\Library\RequestInterface,04 Acme\Library\ResponseInterface;0506class ErrorController implements ActionControllerInterface07{08 public function execute(RequestInterface $request, ResponseInterface $response) {09 echo "Error controller called!";10 }11}

在这种情况下,样品的动作控制器是非常简单的生物,没有做什么特别有用的几个琐碎的信息输出到屏幕以外。这里的关键是演示了如何调用他们通过早期的前端控制器,并传递一些最终的进一步处理请求和响应对象。下面的代码片段显示了如何完成这个简而言之:

01<?php02use Acme\Library\Loader\Autoloader,03 Acme\Library\FrontController,04 Acme\Library\Route,05 Acme\Library\Router,06 Acme\Library\Dispatcher,07 Acme\Library\Request,08 Acme\Library\Response;09 10require_once __DIR__ . "/Acme/Library/Loader/Autoloader.php";11$autoloader = new Autoloader;12$autoloader->register();1314$request = new Request("http://example.com/test/");1516$response = new Response;1718$route1 = new Route("http://example.com/test/",19 "Acme\\Library\\Controller\\TestController");2021$route2 = new Route("http://example.com/error/",22 "Acme\\Library\\Controller\\ErrorController");2324$router = new Router(array($route1, $route2));2526$dispatcher = new Dispatcher;2728$frontController = new FrontController($router, $dispatcher);2930$frontController->run($request, $response);

即使脚本看起来有点拥挤,因为它首先通过保理路线传递到前端控制器的内部,它演示了如何让事情滚动动作控制器,并呼吁在一个非常简单的方式。此外,在这个例子中一个实例TestController的将在运行时调用,因为它有效地匹配的第一条路线。不用说,路由可以定制从上到下,调用其他的动作控制器仅仅是一个问题围绕一个不同的URI传递到请求对象,当然是匹配的路由。尽管所有的前场通过实施过程中所需的设置,这种方法的实际美在于暴露在请求/路由/调度/响应周期中涉及的每个类的模块。那里是不需要到1单片前端控制器的古怪处理了,更不用说了,大多数的整个交易,如,包括对象的事实请求 / 响应对唱,可重复使用在不同的上下文或调换,由更强大的实现。
关闭的思考
虽然前端控制器的学术定义,似乎是相当严格的,因为它描述了作为一个集中的,基于命令的机制,缩小到只调度请求的模式,事实是,在现实世界中的许多方法可以用于获取功能的实现是什么,但稀少。在这个快速的综合报告,只是为了我的目标是演示如何创建从务实的角度来看,至少情侣定制的前端控制器,紧凑,严密实施,路由和调度过程中被打包单一范围内率先呼吁,抓住所有的类,并通过更精细的解决方案第二,在执行问题的过程中进行了解剖,并委托给几个细粒度的类。还是有值得看,比其他展示在这里的每个有自己的优点和缺点,有很多附加选项。像往常一样,你应该拿起完全取决于你的口味。

站长行业门户(www.software8.co)文章,希望大家可以留言建议


读书人网 >编程

热点推荐