PHP面向對象之工作單元

来源:http://www.cnblogs.com/kerryw/archive/2017/06/25/7078504.html
-Advertisement-
Play Games

/* 工作單元 這個模式涉及到了領域模型、數據映射器和標識映射,這裡就統一進行整理和回顧了。 $venue = new \woo\domain\Venue(null,"The Green Tree"); \woo\domain\ObjectWatcher::instance()->performOp... ...


/*
工作單元
這個模式涉及到了領域模型、數據映射器和標識映射,這裡就統一進行整理和回顧了。
$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數據

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • VIM詳細命令有很多,我們選用一些常用的入門命令,足以對付日常的代碼編輯工作了,如果日後有需要使用其他命令,再來查詢也不遲。 vim一般有3種編輯模式,分別是插入模式,正常模式(normal mode),末行模式。 以下主要是在正常模式下的操作,其他模式操作會註明相關模式 1.1 移動游標 h >每 ...
  • 最近手頭上的項目終於忙得差不多了,想起好久沒有更新了的NanUI,再看著每天QQ群未讀消息閃爍的標誌,突然才發現似乎愧對了群里各位喜愛NanUI的朋友們。於是乎,就想趁這幾天有時間,好好的修複一下NanUI已知的BUG,再用有限的時間推進整個項目的進度。 在複習代碼的時候,想起了群里有朋友提出說Na ...
  • 空(None) None可以用來表示某一個變數的值缺失,類似於其他語言中的null。 像其他的空值:0,[]和空的string,布爾變數給的是False而不是True。 結果是: 當一個函數沒有返回任何值時,就會返回None: 結果是: Hi None 字典(Dictionaries) 字典是一種給 ...
  • 在上一節中講解了歸併排序的遞歸版《4.比較排序之歸併排序(遞歸)》,通常來講,遞歸版的歸併排序要更為常用,本節簡單介紹下非遞歸版的歸併排序。思路和遞歸版相同,均為先分解後合併,非遞歸的重點在於如何確定併合理的分解待排序數組。 對於遞歸我們是這麼做的: 對於非遞歸來講,切分的不向遞歸從大到小,非遞歸實 ...
  • 在編寫穩定可靠的軟體服務時經常用到輸出堆棧信息,以便用戶/開發者獲取準確的運行信息。常用在日誌輸出,錯誤報告,異常檢測。本文介紹Linux與Windows下用C++獲取堆棧信息的方法。 ...
  • JSON-lib包(最關鍵的兩個類分別是JSONObject和JSONArray)完成對json的構造和一些基本方法的使用。 二者區別: ①JSONObject構造的字元串是鍵值對形式(key:value),多個鍵值對間以英文逗號連接; ②JSONArray構造的字元串是數組形式([array1,a ...
  • 反射的基本概述 一個class文件被載入到記憶體的時候,JVM就會經行解剖,把這個class文件的所有成員全部解剖出來,然後JVM會創建一個Class對象,把這些成員信息全部都封裝起來,所謂反射就是指:我們獲取到這個Class對象,就相當於獲取到了該類的所有成員信息,我們就能操又該類的所有成員. Ja ...
  • 無意間碰到的一個大神整理的Python學習思維導圖,感覺對初學者理清學習思路大有裨益,非常感謝他的分享。 詳情見:https://woaielf.github.io/2017/06/13/python3-all/ 14 張思維導圖 基礎知識 數據類型 序列 字元串 列表 & 元組 字典 & 集合 條 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...