【深入PHP 面向对象】读书笔记(十九)
来源:互联网 发布:b50板球座子淘宝 编辑:程序博客网 时间:2024/06/15 16:05
12.3.2 应用控制器
应用控制器负责映射请求到命令,并映射命令到视图;使得 Command 类能够集中精力完成包括处理输入、调用应用程序逻辑和处理结果等,而不需要对视图进行调用处理。
我们围绕这张图,来看模式中的参与者(应用控制器、命令和视图)之间的通信过程。
首先是前端控制器 FrontController 使用 AppController 应用控制器接口:
//前端控制器function handleRequest() { $request = new Request(); $app_c = ApplicationRegistry::appController(); while ($cmd = $app_c->getCommand($request)) { $cmd->execute(); } $this->invokeView($request);}function invokeView($target) { include '$target.php'; exit;}
handleRequest() 方法使用 ApplicationRegistry 注册表类的 appController() 静态方法来获取一个 AppController 应用控制器对象,并通过 AppController 应用控制器对象的 getCommand() 方法来获取命令,并通过 Command 对象的 execute() 方法来执行命令。
- 实现概述
针对不同的情况,我们需要加载不同的页面,比如对于一个数据输入表单:如果用户添加错误类型的数据,页面可能会重新显示表单或者显示错误页;如果用户数据填写正确,页面就需要跳转到其他页面。
因此,我们需要在 Command 命令对象中定义一个状态标识来告诉系统当前状态,比如是指示用户填写数据错误,还是用户填写数据合法。这个状态标识定义成一个数组,存储在 Command 超类中:
private static $STATUS_STRINGS = array( 'CMD_DEFAULT' => 0, 'CMD_OK' => 1, 'CMD_ERROR' => 2, 'CMD_INSUFFICIENT_DATA' =>3);
- 配置文件
使用 XML 格式的配置文件:
<control> <view>main</view> <view status="CMD_OK">main</view> <view status="CMD_ERROR">Error</view> <command name="ListVenues"> <view>listvenues</view> </command> <command name="QuickAddVenue"> <classroot name="AddVenue"/> <view>quickadd</view> </command> <command name="AddVenue"> <view>addvenue</view> <status value="CMD_OK"> <forward>AddSpace</forward> </status> </command> <command name="AddSpace"> <view>addspace</view> <status value="CMD_OK"> <forward>ListVenues</forward> </status> </command> </control>
<view>main</view><view status="CMD_OK">main</view><view status="CMD_ERROR">Error</view>
第一个 view 元素是所有命令的默认视图,如果没有指定调用特定的视图,则此处定义的默认视图会被调用。接下来两个 view 元素制定了更为具体的内容,根据命令的状态来调用相应的视图。
<command name="ListVenues"> <view>listvenues</view></command>
command 元素用于根据指定的命令来显示视图。
<command name="QuickAddVenue"> <classroot name="AddVenue"/> <view>quickadd</view></command>
同时配置文件还支持别名,通过别名来指定加载其他命令,来实现加载视图。
<command name="AddVenue"> <view>addvenue</view> <status value="CMD_OK"> <forward>AddSpace</forward> </status></command>
此外,命令可以加载多个视图,并且支持通过命令的状态来确定是否加载指定视图。
- 解析配置文件
定义一个 ApplicationHelper 来实现解析 XML 配置文件,XML 的解析是一个烦琐的工作,具体的解析这里不详细说,只给出大致的解析过程,通过 SimpleXML 解析:
private function getOptions() { $this->ensure(file_exists($this->config),'could not found options file'); $options = @simplexml_load_file($this->config); $map = new ControllerMap(); foreach ($options->control->view as $default_view) { $stat_str = trim($default_view['status']); $status = Command::statuses($stat_str); $map->addView('default',$status,$default_view); } ApplicationRegistry::setControllerMap($map);}
- 存储配置数据
ControllerMap 内封装了3个数组,用于缓存从XML中解析出的数据:
class ControllerMap { private $viewMap = array(); private $forwardMap = array(); private $classrootMap = array(); function addClassroot($command, $classroot) { $this->classrootMap[$command] = $classroot; } function getClassroot($command) { if (isset($this->classrootMap[$command])) { return $this->classrootMap[$command]; } return $command; } function addView($command='default', $status=0, $view) { $this->viewMap[$command][$status] = $view; } function getView($command, $status) { if (isset($this->viewMap[$command][$status])) { return $this->viewMap[$command][$status]; } return null; } function addForward($command='default', $status=0, $newCommand) { $this->forwardMap[$command][$status] = $newCommand; } function getForward($command, $status) { if (isset($this->forwardMap[$command][$status])) { return $this->forwardMap[$command][$status]; } return null; }}
例如这样一段 XML 配置文件:
<command name="AddVenue"> <view>addvenue</view> <status value="CMD_OK"> <forward>AddSpace</forward> </status></command>
在调用进行缓存时,会执行下面的命令:
$map->addView('AddVenue', 0, 'addvenue');$map->addView('AddVenue', 1, 'AddSpace');
在 $viewMap 中存储:
$viewMap['AddVenue'][0] = 'addvenue';$viewMap['AddVenue'][1] = 'AddSpace';
接下来时应用控制器类,实现根据相应的命令来调用相应的视图:
class AppController { private static $base_cmd; private static $default_cmd; private $controllerMap; private $invoked = array(); function __contruct(ControllerMap $map) { $this->controllerMap = $map; if (!self::$base_cmd) { self::$base_cmd = new ReflectionClass("Command"); self::$default_cmd = new DefaultCommand(); } } function getView(Request $req) { $view = $this->getResource($req, 'View'); return $view; } function getForward(Request $req) { $forward = $this->getResource($req, 'Forward'); if ($forward) { $req->setProperty() } return $forward; } /* getResource() 方法执行查找工作,用于转向(getForward())或选择视图(getView())*/ private function getResource(Request $req, $res) { // 得到前一个命令及其状态 $cmd_str = $req->getProperty('cmd'); $previous = $req->getLastCommand(); $status = $previous->getStatus(); if (!$status) { $status = 0; } $acquire = "get$res"; // 得到前一个命令的资源及其状态 $resource = $this->controllerMap->$acquire($cmd_str, $status); // 如果没有查找到指定命令的资源 则查找状态为0的资源 if(!$resource) { $resource = $this->controllerMap->$acquire($cmd_str, 0); } // 如果状态为0的资源也找不到的话,查找默认状态资源 if (!$resource) { $resource = $this->controllerMap->$acquire('default', $status); } // 其他情况获取'default'失败,状态为0 if (!$resource) { $resource = $this->controllerMap->$acquire('default', 0); } return $resource; } /* getCommand() 方法负责返回转向中需要使用的所有命令。*/ function getCommand(Request $req) { $previous = $req->getLastCommand(); if (!$previous) { // 这是本次请求调用的第一个命令 $cmd = $req->getProperty('cmd'); if (!$cmd) { // 如果无法得到命令 则使用默认命令 $req->setProperty('cmd', 'default'); return self::$default_cmd; } } else { // 之前已经执行过一个命令 $cmd = $this->getForward($req); if (!$cmd) { return null; } } // 在$cmd变量中保存着命令名称,并将其解析为Command对象 $cmd_obj = $this->resolveCommand($cmd); if (!$cmd_obj) { throw new AppException("could not resolve $cmd"); } $cmd_class = get_class($cmd_obj); if (isset($this->invoked[$cmd_class])) { throw new AppException("circular forwarding"); } $this->invoked[$cmd_class] = 1; return $cmd_obj; } function resolveCommand($cmd) { $classname = $this->controllerMap->getClassroot($cmd); $filepath = 'woo/command/$classroot.php'; if (file_exists($filepath)) { require_once($filepath); if (class_exists($classname)) { $cmd_class = new ReflectionClass($classname); if ($cmd_class->isSubClassOf(self::$base_cmd)) { return $cmd_class->newInstance(); } } } return null; }}
getResource() 方法执行查找工作,用于转向(getForward())或选择视图(getView()),它会优先查找最具体的字符串和状态标识的组合,然后才搜索通用的组合。
getCommand() 方法负责返回转向中需要使用的所有命令。
- Command 基类
abstract class Command { private static $STATUS_STRINGS = array( 'CMD_DEFAULT' => 0, 'CMD_OK' => 1, 'CMD_ERROR' => 2, 'CMD_INSUFFICIENT_DATA' =>3 ); private $status = 0; final function __contruct(){} /*execute() 方法使用抽象方法 doExecute() 返回的值来设置状态标识,并将它保存在 Request 对象中。*/ function execute(Request $req) { $this->status = $this->doExecute($req); $req->setCommand($this); } /*getStatus() 用于当前的状态标识*/ function getStatus() { return $this->status; } /*statuses() 方法用于将字符串状态转换成相应的数字*/ static function statuses($str='CMD_DEFAULT'){ return self::$STATUS_STRINGS[$str]; } abstract function doExecute(Request $req);}
Command 类定义了一个状态字符串数组 $STATUS_STRINGS。statuses() 方法用于将字符串状态转换成相应的数字,getStatus() 用于当前的状态标识,execute() 方法使用抽象方法 doExecute() 返回的值来设置状态标识,并将它保存在 Request 对象中。
- 一个具体的 Command 类:
class AddVenue extends Command { function doExecute(Request $req) { $name = $req->getProperty('venue_name'); if (!$name) { $request->addFeedback('no name provided'); return self::statuses('CMD_INSUFFICIENT_DATA'); } else { $venue_obj = new Venue(null, $name); $request->setObject('venue', $venue_obj); $request->addFeedback('$name added({$venue_obj->getId()})'); return self::statuses('CMD_OK'); } }}
具体的 venue 类:
class Venue { private $id; private $name; function __construct($id, $name) { $this->id = $id; $this->name = $name; } function getName() { return $this->name; } function getId() { return $this->id; }}
这样一个基本的应用控制器就建立起来了,系统的响应由配置文件决定。
- 【深入PHP 面向对象】读书笔记(十九)
- 【深入PHP 面向对象】读书笔记(一)
- 【深入PHP 面向对象】读书笔记(二)
- 【深入PHP 面向对象】读书笔记(三)
- 【深入PHP 面向对象】读书笔记(四)
- 【深入PHP 面向对象】读书笔记(五)
- 【深入PHP 面向对象】读书笔记(六)
- 【深入PHP 面向对象】读书笔记(七)
- 【深入PHP 面向对象】读书笔记(八)
- 【深入PHP 面向对象】读书笔记(九)
- 【深入PHP 面向对象】读书笔记(十)
- 【深入PHP 面向对象】读书笔记(十一)
- 【深入PHP 面向对象】读书笔记(十二)
- 【深入PHP 面向对象】读书笔记(十三)
- 【深入PHP 面向对象】读书笔记(十四)
- 【深入PHP 面向对象】读书笔记(十五)
- 【深入PHP 面向对象】读书笔记(十六)
- 【深入PHP 面向对象】读书笔记(十七)
- 获取当前设备所有应用基础信息
- Direct X入门
- 关于python中if '_name_' =='_main_'
- 10月19日云栖精选夜读:我的2017云栖之行
- descendantFocusability
- 【深入PHP 面向对象】读书笔记(十九)
- C# 数组
- 1-3·Linux基础命令、目录、链接
- Ubuntu下如何在桌面创建一个应用程序的“快捷方式”?
- 【入门篇】Activiti配置数据库执行策略
- Android设备中各种分辨率清单
- 你的Wi-Fi 还安全吗?全球重大漏洞WPA2 KRACK 详细分析报告
- css-继承、优先级、层叠
- 从多张图片重建3D模型(瞎七瞎八写了好多)