您好,欢迎访问一九零五行业门户网

[php]标记投射和工作单元

[php]标记映射和工作单元
    标记映射
系统中可能存在两个值相同,但又不是同一个引用的对象,这样的重复对象可能是从数据库中读出来的,这样就造成了不必要的查询。
        标记映射是一个类objectwatcher,它负责管理进程中的领域对象,以保证进程中不出现重复对象。
        标记映射可以防止重新读取数据库查询数据,只有当objectwatcher类中不存在标记映射对应的对象时才去查询数据库。这样就保证了在一个进程中,一条数据只对应一个对象。
        代码很容易懂,都是一些存取数组值的操作。
objectwatcher代码:
namespace demo\domain;use \demo\domain\domainobject;/** * 标记映射 */class objectwatcher { private static $instance; // 标记映射 private $all = array(); private function __construct() { } public static function getinstance() { if (!isset(self::$instance)) { self::$instance = new self(); } return self::$instance; } /** * 获得对象对应的键值 * @param domainobject $obj */ public function getgobalkey(domainobject $obj) { $key = get_class($obj) . '_' . $obj->getid(); return $key; } /** * 添加到all * @param domainobject $obj */ public static function add(domainobject $obj) { $instance = self::getinstance(); $key = $instance->getgobalkey($obj); $instance->all[$key] = $obj; } /** * 从all中删除 * @param domainobject $obj */ public static function delete(domainobject $obj) { $instance = self::getinstance(); $key = $instance->getgobalkey($obj); unset($instance->all[$key]); } /** * 判断标记是否存在 * @param string $classname * @param int $id */ public static function exists($classname, $id) { $instance = self::getinstance(); $key = {$classname}_{$id}; if (isset($instance->all[$key])) { return $instance->all[$key]; } return null; }}
那么在哪里做标记呢?当然是生成查询对象的地方,分别有mapper::find()、mapper::insert()、mapper::createobject()。 mapper中新增加了addtomap()和getfrommap()。(其它方法没有改变,所以看以忽略吧。)
        mapper代码:
namespace demo\mapper;use \demo\base\appexception;use \demo\base\applicationregistry;use \demo\domain\domainobject;use \demo\domain\objectwatcher;/** * mapper */abstract class mapper { // pdo protected static $pdo; // config protected static $dsn, $dbusername, $dbpassword; // pdo选项 protected static $options = array( \pdo::mysql_attr_init_command => 'set names utf8', \pdo::attr_errmode, \pdo::errmode_exception, ); public function __construct() { if (!isset(self::$pdo)) { // applicationregistry获取数据库连接信息 $appregistry = applicationregistry::getinstance(); self::$dsn = $appregistry->getdsn(); self::$dbusername = $appregistry->getdbusername(); self::$dbpassword = $appregistry->getdbpassword(); if (!self::$dsn || !self::$dbusername || !self::$dbpassword) { throw new appexception('mapper init failed!'); } self::$pdo = new \pdo(self::$dsn, self::$dbusername, self::$dbpassword, self::$options); } } /** * 查找指定id * @param int $id */ public function findbyid($id) { // 从objectwatcher中获取 $obj = $this->getfrommap($id); if (!is_null($obj)) { return $obj; } $pstmt = $this->getselectstmt(); $pstmt->execute(array($id)); $data = $pstmt->fetch(); $pstmt->closecursor(); if (!is_array($data) || !isset($data['id'])) { return $obj; } $obj = $this->createobject($data); return $obj; } /** * 返回collection */ public function findall() { $pstmt = $this->getselectallstmt(); $pstmt->execute(array()); $raws = $pstmt->fetchall(\pdo::fetch_assoc); $collection = $this->getcollection($raws); return $collection; } /** * 插入数据 * @param \demo\domain\domainobject $obj */ public function insert(domainobject $obj) { $flag = $this->doinsert($obj); // 保存或者更新objectwatcher的$all[$key]的对象 $this->addtomap($obj); return $flag; } /** * 更新对象 * @param \demo\domain\domainobject $obj */ public function update(\demo\domain\domainobject $obj) { $flag = $this->doupdate($obj); return $flag; } /** * 删除指定id * @param int $id */ public function deletebyid($id) { $pstmt = $this->getdeletestmt(); $flag = $pstmt->execute(array($id)); return $flag; } /** * 生成一个$data中值属性的对象 * @param array $data */ public function createobject(array $data) { // 从objectwatcher中获取 $obj = $this->getfrommap($data['id']); if (!is_null($obj)) { return $obj; } // 创建对象 $obj = $this->docreateobject($data); // 添加到objectwatcher $this->addtomap($obj); return $obj; } /** * 返回对应key标记的对象 * @param int $id */ private function getfrommap($id) { return objectwatcher::exists($this->gettargetclass(), $id); } /** * 添加对象到标记映射objectwatcher类 * @param domainobject $obj */ private function addtomap(domainobject $obj) { return objectwatcher::add($obj); } /** * 返回子类collection * @param array $raw */ public function getcollection(array $raws) { return $this->getfactory()->getcollection($raws); } /** * 返回子类持久化工厂对象 */ public function getfactory() { return persistancefactory::getfactory($this->gettargetclass()); } protected abstract function doinsert(\demo\domain\domainobject $obj); protected abstract function docreateobject(array $data); protected abstract function getselectstmt(); protected abstract function getselectallstmt(); protected abstract function doupdate(\demo\domain\domainobject $obj); protected abstract function getdeletestmt(); protected abstract function gettargetclass();}
大部分代码是之前的,修改的只是一小部分。下面图一张:
现在,当mapper从数据库中取出的数据映射成的对象都被标记到objectwatcher了,而且不需要对对象手动操作标记到objectwatcher,mapper就已经帮你完成了。这样带来的好处是可以减少对数据库的操作和新对象的创建,比如find、createobject。但这也许可能带来问题,如果你的程序需要并发处理数据,那么被标记的对象数据就可能不一致了,你在这个时候可能需要对数据加锁。
工作单元
          有些时候,我们可能没有改变数据的任何值却向数据库多次保存该数据,这当然是不必要的吧。工作单元可以使你只保存那些需要的对象。工作单元可以在一次请求即将结束时,把在这次请求中发生变化的对象保存到数据库中。一次请求的最后是在控制器(controller)调用完command和view之后,那么我们就可以在这里让工作单元执行任务。  
        标记映射的作用是在处理过程开始时向数据库加载不必要的对象,而工作单元则是在处理过程之后防止不必要的对象保存到数据库中。这两个工作方式就像是互补的。
        为了判断哪些数据库的操作是必要的,那就需要跟踪与对象相关的各种事件(比如:setter()重新设置了对象的属性值)。跟踪工作当然最好放在被跟踪的对象中。
        修改过的objectwatcher类:
namespace demo\domain;use \demo\domain\domainobject;/** * 标记映射 */class objectwatcher { private static $instance; // 标记映射 private $all = array(); // 保存新建对象 private $new = array(); // 保存被修改过的对象(“脏对象”) private $dirty = array(); // 保存删除对象 private $delete = array(); private function __construct() { } public static function getinstance() { if (!isset(self::$instance)) { self::$instance = new self(); } return self::$instance; } /** * 获得对象对应的键值 * @param domainobject $obj */ public function getgobalkey(domainobject $obj) { $key = get_class($obj) . '_' . $obj->getid(); return $key; } /** * 添加到all * @param domainobject $obj */ public static function add(domainobject $obj) { $instance = self::getinstance(); $key = $instance->getgobalkey($obj); $instance->all[$key] = $obj; } /** * 从all中删除 * @param domainobject $obj */ public static function delete(domainobject $obj) { $instance = self::getinstance(); $key = $instance->getgobalkey($obj); unset($instance->all[$key]); } /** * 添加到new * @param domianobject $obj */ public static function addnew(domainobject $obj) { $instance = self::getinstance(); $instance->new[] = $obj; } /** * 添加到dirty * @param domianobject $obj */ public static function adddirty(domainobject $obj) { $instance = self::getinstance(); if (!in_array($obj, $instance->dirty, true)) { $instance->dirty[$instance->getgobalkey($obj)] = $obj; } } /** * 添加到delete * @param domainobject $obj */ public static function adddelete(domainobject $obj) { $instance = self::getinstance(); $instance->delete[$instance->getgobalkey($obj)] = $obj; } /** * 清除标记dirty new delete * @param domainobject $obj */ public static function addclean(domainobject $obj) { $instance = self::getinstance(); // unset删除保存的对象 unset($instance->dirty[$instance->getgobalkey($obj)]); unset($instance->delete[$instance->getgobalkey($obj)]); // 删除new中的对象 $instance->new = array_filter($instance->new, function($a) use ($obj) { return !($a === $obj); }); } /** * 判断标记是否存在 * @param string $classname * @param int $id */ public static function exists($classname, $id) { $instance = self::getinstance(); $key = {$classname}_{$id}; if (isset($instance->all[$key])) { return $instance->all[$key]; } return null; } /** * 对new dirty delete 中的标记对象执行操作 */ public function performoperations() { $instance = self::getinstance(); // new foreach ($instance->new as $obj) { $obj->finder()->insert($obj); } // dirty foreach ($instance->dirty as $obj) { $obj->finder()->update($obj); } // delete foreach ($instance->delete as $obj) { $obj->finder()->delete($obj); } $this->new = array(); $this->dirty = array(); $this->delete = array(); }}
objectwatcher依然是标记映射,只是在这里增加了跟踪系统中对象的变化的功能。objectwatcher类提供了查找、删除、添加对象到数据库的机制。
        由于objectwatcher的操作上对对象的操作,所以由这些对象自己来来执行objectwatcher是很适合的。
        修改过的domainobject类(marknew()、markdirty()、markdelete()、markclean()):
namespace demo\domain;use \demo\domain\helperfactory;use \demo\domain\objectwatcher;/** * 领域模型抽象基类 */abstract class domainobject { protected $id = -1; public function __construct($id = null) { if (is_null($id)) { // 标记为new 新建 $this->marknew(); } else { $this->id = $id; } } public function getid() { return $this->id; } public function setid($id) { $this->id = $id; $this->markdirty(); } public function marknew() { objectwatcher::addnew($this); } public function markdirty() { objectwatcher::adddirty($this); } public function markdeleted() { objectwatcher::adddelete($this); } public function markclean() { objectwatcher::addclean($this); } public static function getcollection($type) { return helperfactory::getcollection($type); } public function collection() { return self::getcollection(get_class($this)); } public static function getfinder($type) { return helperfactory::getfinder($type); } public function finder() { return self::getfinder(get_class($this)); }}
domainobject和objectwatcher的关系图一张:
修改过的mapper(和上面相同部分略去了,太占位子了):
/** * mapper */abstract class mapper { //... /** * 查找指定id * @param int $id */ public function findbyid($id) { // 从objectwatcher中获取 $obj = $this->getfrommap($id); if (!is_null($obj)) { return $obj; } $pstmt = $this->getselectstmt(); $pstmt->execute(array($id)); $data = $pstmt->fetch(); $pstmt->closecursor(); if (!is_array($data) || !isset($data['id'])) { return $obj; } $obj = $this->createobject($data); return $obj; } /** * 插入数据 * @param \demo\domain\domainobject $obj */ public function insert(domainobject $obj) { $flag = $this->doinsert($obj); // 保存或者更新objectwatcher的$all[$key]的对象 $this->addtomap($obj); $obj->markclean(); // 调试用的 echo 'insert :' . get_class($obj) . '_' . $obj->getname() . '_' . $obj->getid() .'
'; return $flag; } /** * 更新对象 * @param \demo\domain\domainobject $obj */ public function update(\demo\domain\domainobject $obj) { $flag = $this->doupdate($obj); $obj->markclean(); // 调试用的 echo 'update :' . get_class($obj) . '_' . $obj->getname() . '_' . $obj->getid() .'
'; return $flag; } /** * 生成一个$data中值属性的对象 * @param array $data */ public function createobject(array $data) { // 从objectwatcher中获取 $obj = $this->getfrommap($data['id']); if (!is_null($obj)) { return $obj; } // 创建对象 $obj = $this->docreateobject($data); // 添加到objectwatcher $this->addtomap($obj); // 清除new标记 objectwatcher::addclean($obj); return $obj; } //...}
可以看到mapper中修改的部分都是有改变对象的事件发生,即find()、update()、insert()、delete()。
        对象的变化都能被跟踪到了,那么应该在哪里处理这些变化过的对象(“脏数据”)呢?上面说到了,应该在一次请求即将完成的时候。
        一次请求即将结束时,controller中调用工作单元(同样省略了没改变的代码):
namespace demo\controller;/** * controller */class controller { // ... private function handlereuqest() { $request = new \demo\controller\request(); $appcontroller = \demo\base\applicationregistry::getinstance()->getappcontroller(); // 执行完所有command,有可能存在forward while ($cmd = $appcontroller->getcommand($request)) { // var_dump($cmd); $cmd->execute($request); // 把当前command设为已执行过 $request->setlastcommand($cmd); } // 工作单元执行任务 objectwatcher::getinstance()->performoperations(); // 获取视图 $view = $appcontroller->getview($request); // 显示视图 $this->invokeview($view); } // ...}
objectwatcher::getinstance()->performoperations()
好的,现在来个使用例子吧:
namespace demo\command;use demo\domain\classroom;use demo\base\applicationregistry;use demo\domain\objectwatcher;use demo\domain\helperfactory;class test extends command { protected function doexecute(\demo\controller\request $request) { $crmapper = helperfactory::getfinder('demo\domain\classroom'); // 新创建的对象 marknew() $cra = new classroom(); $cra->setname('四年(3)班'); // 修改后的“脏”数据 $crb = $crmapper->findbyid(1); $crb->setname(五年(2)班); }}
输入的url:localhost/demo/runner.php?cmd=test
输出结果与预期的一样:insert :demo\domain\classroom_四年(3)班_58update :demo\domain\classroom_五年(2)班_1
现在对领域对象的管理有了较大的改进了。还有,我们使用模式的目的是提高效率,而不是降低效率。
其它类似信息

推荐信息