PHP面向对象之工作单元

/*
工作单元
这个模式涉及到了领域模型、数据映射器和标识映射,这里就统一进行整理和回顾了。
$venue = new \woo\domain\Venue(null,"The Green Tree");
\woo\domain\ObjectWatcher::instance()->performOperations();
现在以上面的二行客户端代码为切入点大概的叙述一下这个模式是怎么工作的。
第一句在使用领域模型对象创建一个对象的时候,它就调用了标识映射ObjectWatcher类
将自己标记为一个需要新增的对象。第二句的performOperations方法将保存在标识映射器的属性$new中的对象
插入到了数据库中。注意它内部调用的$obj->finder()方法是领域模式中通过HelperFactory工厂类生成一个相对应的数据映射器类并return过来。
HelperFactory这个类下面没有具体实现(原文也没有实现),其实就是根据参数传入的类的类型使用条件分支创建对应的数据映射器。
下面直接看代码和注释进行理解。
*/

namespace woo\domain;

//标识映射
class ObjectWatcher{

    private $all = array();                //存放对象的小仓库
    private $dirty = array();            //存放需要在数据库中修改的对象
    private $new = array();                //存放需要在数据库中新增的对象
    private $delete = array();            //存放需要在数据库中删除的对象
    private static $instance;            //单例

    private function __construct (){}

    static function instance(){
        if(!self::$instance){
            self::$instance = new ObjectWatcher();
        }
        return self::$instance;
    }

    //获取一个唯一的标识,这里采用了领域类类名+ID的方式创建一个唯一标识,避免多个数据库表调用这个类时出现ID重复的问题
    function globalKey(DomainObject $obj){
        $key = get_class($obj) . "." . $obj->getId();
        return $key;
    }

    //添加对象
    static function add(DomainObject $obj){
        $inst = self::instance();
        $inst->all[$inst->globalKey($obj)] = $obj;
    }

    //获取对象
    static function exists($classname,$id){
        $inst = self::instance();
        $key = "$classname.$id";
        if(isset($inst->all[$key]){
            return $inst->all[$key];
        }
        return null;
    }

    //标记为需要删除的对象
    static function addDelete(DomainObject $obj){
        $self = self::instance();
        $self->delete[$self->globalKey($obj)] = $obj;
    }

    //标记为需要修改的对象
    static function addDirty(DomainObject $obj){
        $inst = self::instance();
        if(!in_array($obj,$inst->new,true)){
            $inst->dirty[$inst->globalKey($obj)] = $obj;
        }
    }

    //标记为需要新增的对象
    static function addNew(DomainObject $obj){
        $inst = self::instance();
        $inst->new[] = $obj;
    }

    //标记为干净的对象
    static function addClean(DomainObject $obj){
        $self = self::instance();
        unset($self->delete[$self->globalKey($obj)]);
        unset($self->dirty[$self->globalKey($obj)]);
        $self->new = array_filter($self->new,function($a) use($obj) {return !($a === $obj);});
    }

    //将上述需要增删改的对象与数据库交互进行处理
    function performOperations(){
        foreach($this->dirty as $key=>$obj){
            $obj->finder()->update($obj);        //$obj->finder()获取一个数据映射器
        }
        foreach($this->new as $key=>$obj){
            $obj->finder()->insert($obj);
        }
        $this->dirty = array();
        $this->new = array();
    }
}

//领域模型
abstract class DomainObject{            //抽象基类

    private $id = -1;

    function __construct ($id=null){
        if(is_null($id)){
            $this->markNew();            //初始化时即被标记为需要新增的对象了
        } else {
            $this->id = $id;
        }
    }

    //调用了标识映射的标记对象的方法
    function markNew(){
        ObjectWatcher::addNew($this);
    }

    function markDeleted(){
        ObjectWatcher::addDelete($this);
    }

    function markDirty(){
        ObjectWatcher::addDirty($this);
    }

    function markClean(){
        ObjectWatcher::addClean($this);
    }

    function setId($id){
        $this->id = $id;
    }

    function getId(){
        return $this->id;
    }

    function finder(){
        return self::getFinder(get_class($this));
    }

    //通过工厂类来实例化一个特定类型的数据映射器对象,例如VenueMapper
    //这个对象将被标识映射器中的performOperations方法调用用于与数据库交互进行增删改的操作
    static function getFinder($type){
        return HelperFactory::getFinder($type);
    }

}

class Venue extends DomainObject {
    private $name;
    private $spaces;

    function __construct ($id = null,$name=null){
        $this->name= $name;
        $this->spaces = self::getCollection(‘\\woo\\domain\\space‘);
        parent::__construct($id);
    }

    function setSpaces(SpaceCollection $spaces){
        $this->spaces = $spaces;
        $this->markDirty();                        //标记为需要修改的对象
    }

    function addSpace(Space $space){
        $this->spaces->add($space);
        $space->setVenue($this);
        $this->markDirty();                        //标记为需要修改的对象
    }

    function setName($name_s){
        $this->name = $name_s;
        $this->markDirty();                        //标记为需要修改的对象
    }

    function getName(){
        return $this->name;
    }
}

//领域模型
class Space extends DomainObject{
    //.........
    function setName($name_s){
        $this->name = $name_s;
        $this->markDirty();
    }

    function setVenue(Venue $venue){
        $this->venue = $venue;
        $this->markDirty();
    }
}

//数据映射器
abstract class Mapper{

    abstract static $PDO;        //操作数据库的pdo对象
    function __construct (){
        if(!isset(self::$PDO){
            $dsn = \woo\base\ApplicationRegistry::getDSN();
            if(is_null($dsn)){
                throw new \woo\base\AppException("no dns");
            }
            self::$PDO = new \PDO($dsn);
            self::$PDO->setAttribute(\PDO::ATTR_ERRMODE,\PDO::ERRMODE_EXCEPTION);
        }
    }

    //获取标记的对象
    private function getFroMap($id){
        return \woo\domain\ObjectWatcher::exists($this->targetClass(),$id);
    }

    //新增标记的对象
    private function addToMap(\woo\domain\DomainObject $obj){//////
        return \woo\domain\ObjectWatcher::add($obj);
    }

    //将数据库数据映射为对象
    function createObject($array){
        $old = $this->getFromMap($array[‘id‘]);
        if($old){return $old;}
        $obj = $this->doCreateObject($array);
        $this->addToMap($obj);
        $obj->markClean();
        return $obj;
    }

    function find($id){                                //通过ID从数据库中获取一条数据并创建为对象
        $old = $this->getFromMap($id);
        if($old){return $old}                        

        $this->selectStmt()->execute(array($id));
        $array= $this->selectStmt()->fetch();
        $this->selectStmt()->closeCursor();
        if(!is_array($array)){
            return null;
        }
        if(!isset($array[‘id‘])){
            return null;
        }
        $object = $this->createObject($array);
        $this->addToMap($object);
        return $object;
    }

    function insert(\woo\domain\DomainObject $obj){            //将对象数据插入数据库
        $this->doInsert($obj);
        $this->addToMap($obj);
    }

    //需要在子类中实现的各个抽象方法
    abstract function targetClass();                                        //获取类的类型
    abstract function update(\woo\domain\DomainObject $objet);                //修改操作
    protected abstract function doCreateObject(array $array);                //创建对象
    protected abstract function selectStmt();                                //查询操作
    protected abstract function doInsert(\woo\domain\DomainObject $object);    //插入操作

}

class VenueMapper extends Mapper {
    function __construct (){
        parent::__construct();
        //预处理对象
        $this->selectStmt = self::$PDO->prepare("select * from venue where id=?");
        $this->updateStmt = self::$PDO->prepare("update venue set name=?,id=? where id=?");
        $this->insertStmt = self::$PDO->prepare("insert into venue (name) values(?)");
    }

    protected function getCollection(array $raw){        //将Space数组转换成对象集合
        return new SpaceCollection($raw,$this);
    }

    protected function doCreateObject (array $array){    //创建对象
        $obj = new \woo\domain\Venue($array[‘id‘]);
        $obj->setname($array[‘name‘]);
        return $obj;
    }

    protected function doInsert(\woo\domain\DomainObject $object){  //将对象插入数据库
        print ‘inserting‘;
        debug_print_backtrace();
        $values = array($object->getName());
        $this->insertStmt->execute($values);
        $id = self::$PDO->lastInsertId();
        $object->setId($id);
    }

    function update(\woo\domain\DomainObject $object){        //修改数据库数据
        print "updation\n";
        $values = array($object->getName(),$object->getId(),$object->getId());
        $this->updateStmt->execute($values);
    }

    function selectStmt(){                    //返回一个预处理对象
        return $this->selectStmt;
    }

}

//客户端
$venue = new \woo\domain\Venue(null,"The Green Tree");                //在初始化时就被标记为新增对象了
$venue->addSpace(new \woo\domain\Space(null,"The Space Upstairs"));    //这二行addSpace方法因为venue已经被标记新增所以不会再标记为修改对象,但是space在初始化的时候会被标记为新增对象
$venue->addSpace(new \woo\domain\Space(null,"The Bar Stage"));      
\woo\domain\ObjectWatcher::instance()->performOperations();            //与数据库交互新增一条Venue数据,以及二条space数据
时间: 2024-10-16 05:32:39

PHP面向对象之工作单元的相关文章

.NET应用架构设计—工作单元模式(摆脱过程式代码的重要思想,逆袭DDD)

阅读目录: 1.背景介绍 2.过程式代码的真正困境 3.工作单元模式的简单示例 4.总结 1.背景介绍 一直都在谈论面向对象开发,但是开发企业应用系统时,使用面向对象开发最大的问题就是在于,多个对象之间的互操作需要涉及数据库操作.两个业务逻辑对象彼此之间需要互相调用,如果之间的互相操作是在一个业务事务范围内的,很容易完成,但是如果本次业务逻辑操作涉及到多个业务对象一起协作完成时问题就来了. 在以往,我们使用过程式的代码(事务脚本模式),将所有与本次业务事务范围内相关的所有逻辑都写在一个大的代码中

应用程序框架实战十九:工作单元层超类型

上一篇介绍了DDD聚合以及与并发相关的各种锁机制,本文将介绍另一个核心元素——工作单元,它是实现仓储的基础. 什么是工作单元 维护受业务事务影响的对象列表,并协调变化的写入和并发问题的解决. 这是<企业应用架构模式>中给出的定义,不过看上去有点抽象.它大概的意思是说,对多个操作进行打包,记录对象上的所有变化,并在最后提交时一次性将所有变化通过系统事务写入数据库. 当然,工作单元不一定是针对数据库的,不过大部分程序员还是工作在关系数据库中,所以我默认你也在使用关系数据库,由此产生的不准确性你就不

MVC+EF 理解和实现仓储模式和工作单元模式

MVC+EF 理解和实现仓储模式和工作单元模式 原文:Understanding Repository and Unit of Work Pattern and Implementing Generic Repository in ASP.NET MVC using Entity Framework 文章介绍 在这篇文章中,我们试着来理解Repository(下文简称仓储)和Unit of Work(下文简称工作单元)模式.同时我们使用ASP.NET MVC和Entity Framework 搭

【.Net设计模式系列】工作单元(Unit Of Work)模式 ( 二 )

回顾 在上一篇博客[.Net设计模式系列]仓储(Repository)模式 ( 一 ) 中,通过各位兄台的评论中,可以看出在设计上还有很多的问题,在这里特别感谢 @横竖都溢 @ 浮云飞梦 2位兄台对博文中存在的问题给予指出,并提供出好的解决方案,同时也感谢其他园友的支持.欢迎各位园友对博文中出现的错误或者是设计误区给予指出,一方面防止“误人子弟”,另一方面则可以让大家共同成长. 对于上一篇博客,只是给大家提供了一种对于小型项目数据访问层的一种实现方式,通过Sql语句和传递参数来实现CRUD.并未

ABP官方文档翻译 3.6 工作单元

工作单元 介绍 ABP中的连接和事务管理 传统的工作单元方法 控制工作单元 UnitOfWork特性 IUnitOfWorkManager 工作单元详情 禁用工作单元 无事务工作单元 一个工作单元方法调用另一个 工作单元范围 自动保存更改 IRepository.GetAll()方法 工作单元特性限制 选项 方法 SaveChanges 事件 介绍 在使用数据库的应用中,连接和事务管理是最重要的概念之一.什么时候打开连接,什么时候开始一个事务,如何释放连接等等.ABP使用工作单元系统来管理连接和

Abp之工作单元与事务

环境:Abp1.2 疑问:没有调用工作单元的SaveChanges方法引起的事务提交时机的问题. 例如:有一个应用服务代码如下: public void CreatePhrase(PhraseCreateDto input) {var phrase = Mapper.Map<Phrase>(input); phrase.Id = Guid.NewGuid(); _phraseRepository.Insert(phrase); } 根据用户提交数据插入一条记录,但在方法末未显式调用SaveCh

在MVC程序中,使用泛型仓储模式和工作单元实现增删查改

在这片文章中,我将自己动手为所有的实体:写一个泛型仓储类,还有一个工作单元. 工作单元的职责就是:为每一个实体,创建仓储实例.仓储(仓库)的职责:增删查改的功能实现. 我们将会在控制器中,创建工作单元类(UnitOfWork)的实例,然后根据实体,创建仓储实例,再就是使用仓储里面的方法,做操作了. 下面的图中,解释了,仓储和EF 数据上文的关系,在这个图里面,MVC控制器和仓储之间的交互,是通过工作单元来进行的,而不是直接和EF接触. 那么你可能就要问了,为什么要使用工作单元??? 工作单元,就

ABP领域层——工作单元(Unit Of work)

ABP领域层——工作单元(Unit Of work) 点这里进入ABP系列文章总目录 基于DDD的现代ASP.NET开发框架--ABP系列之12.ABP领域层——工作单元(Unit Of work) ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称. ABP的官方网站:http://www.aspnetboilerplate.com ABP在Github上的开源项目:https://github.com/aspnetboilerplate 通用

[.NET领域驱动设计实战系列]专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现

一.前言 在前面2篇博文中,我分别介绍了规约模式和工作单元模式,有了前面2篇博文的铺垫之后,下面就具体看看如何把这两种模式引入到之前的网上书店案例里. 二.规约模式的引入 在第三专题我们已经详细介绍了什么是规约模式,没看过的朋友首先去了解下.下面让我们一起看看如何在网上书店案例中引入规约模式.在网上书店案例中规约模式的实现兼容了2种模式的实现,兼容了传统和轻量的实现,包括传统模式的实现,主要是为了实现一些共有规约的重用,不然的话可能就要重复写这些表达式.下面让我们具体看看在该项目中的实现. 首先