資料來源架構模式之資料對應器

來源:互聯網
上載者:User
前面分別介紹了資料來源架構模式之表資料入口、資料來源架構模式之行和資料入口資料來源架構模式之活動記錄,相較於這三種資料來源架構模式,資料對應器顯得更加“高大上”。

一、概念

資料對應器(Data Mapper):在保持對象和資料庫(以及映射器本身)彼此獨立的情況下,在二者之間移動資料的一個映射器層。概念永遠都是抽象的,簡單的說,資料對應器就是一個負責將資料對應到對象的類資料。

二、為什麼要使用資料對應器?

資料對應器實現起來比前三種模式都要複雜,那為什麼還要使用它呢?

對象間的組織關係和關聯式資料庫中的表是不同的。資料庫表可以看成是由行與列組成的格子,表中的一行可以通過外鍵和另一個表(甚至同一個表)中的一行關聯,而對象的組織關係更為複雜:一個對象可能包含其他對象;不同的資料結構可能通過不同的方式組織相同的對象。

對象和關聯式資料庫之間的這種分歧被稱為“對象關係阻抗不匹配”或“阻抗不匹配”。

資料對應器可以很好地解決這個問題,由它來負責對象和關聯式資料庫兩者資料的轉換,從而有效地在領域模型中隱藏資料庫操作並管理資料庫轉換中不可以避免的衝突。

三、簡單實現資料對應器

Php代碼

<?php  //領域抽象類別  abstract class DomainObject {      private $id = -1;        function __construct( $id=null ) {          if ( is_null( $id ) ) {              $this->markNew();          } else {              $this->id = $id;          }      }        function getId( ) {          return $this->id;      }        static function getCollection( $type ) {          //這裡通過一個工廣產生此對象對應的數組資料對象          return HelperFactory::getCollection( $type );       }         function collection() {          return self::getCollection( get_class( $this ) );      }        function finder() {          return self::getFinder( get_class( $this ) );      }        static function getFinder( $type ) {          //這裡通過一個工廠產生此對象對應的map對象          return HelperFactory::getFinder( $type );       }        function setId( $id ) {          $this->id = $id;      }        function __clone() {          $this->id = -1;      }  }    //場所類  class Venue extends DomainObject {      private $name;      private $spaces;        function __construct( $id=null, $name=null ) {          $this->name = $name;          parent::__construct( $id );      }            function setSpaces( SpaceCollection $spaces ) {          $this->spaces = $spaces;      }         function getSpaces() {          if ( ! isset( $this->spaces ) ) {              //建立對應的SpaceMapper對象              $finder = self::getFinder( 'Space' );               $this->spaces = $finder->findByVenue( $this->getId() );              //$this->spaces = self::getCollection("Space");          }          return $this->spaces;      }         function addSpace( Space $space ) {          $this->getSpaces()->add( $space );          $space->setVenue( $this );      }        function setName( $name_s ) {          $this->name = $name_s;      }        function getName( ) {          return $this->name;      }            static function findAll() {          $finder = self::getFinder( __CLASS__ );           return $finder->findAll();      }      static function find( $id ) {          $finder = self::getFinder( __CLASS__ );           return $finder->find( $id );      }    }      abstract class Mapper{      protected static $PDO;       function __construct() {             if ( ! isset(self::$PDO) ) {               //此處可加緩衝              self::$PDO = new PDO( $dsn );              self::$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);          }      }        private function getFromMap( $id ) {          //從記憶體取出此$id的DomainObject對象      }        private function addToMap( DomainObject $obj ) {          //將此DomainObject對象加入到記憶體      }        function find( $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 );          return $object;       }        function findAll( ) {          $this->selectAllStmt()->execute( array() );          return $this->getCollection( $this->selectAllStmt()->fetchAll( PDO::FETCH_ASSOC ) );      }         function createObject( $array ) {          $old = $this->getFromMap( $array['id']);          if ( $old ) { return $old; }          $obj = $this->doCreateObject( $array );          $this->addToMap( $obj );          return $obj;      }        function insert( DomainObject $obj ) {          $this->doInsert( $obj );           $this->addToMap( $obj );      }        protected abstract function getCollection( array $raw );      protected abstract function doCreateObject( array $array );      protected abstract function doInsert( DomainObject $object );      protected abstract function targetClass();      protected abstract function selectStmt( );      protected abstract function selectAllStmt( );  }    class VenueMapper extends Mapper {        function __construct() {          parent::__construct();          $this->selectAllStmt = self::$PDO->prepare(                               "SELECT * FROM venue");          $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( ? )");      }             function getCollection( array $raw ) {          //這裡簡單起見用個對象數組          $ret = array();          foreach ($raw as $value) {              $ret[] = $this->createObject($value);          }          return $ret;      }        protected function doCreateObject( array $array ) {          $obj = new Venue( $array['id'] );          $obj->setname( $array['name'] );          //$space_mapper = new SpaceMapper();          //$space_collection = $space_mapper->findByVenue( $array['id'] );          //$obj->setSpaces( $space_collection );          return $obj;      }        protected function targetClass() {          return "Venue";      }        protected function doInsert( DomainObject $object ) {          $values = array( $object->getname() );           $this->insertStmt->execute( $values );          $id = self::$PDO->lastInsertId();          $object->setId( $id );      }            function update( DomainObject $object ) {          $values = array( $object->getname(), $object->getid(), $object->getId() );           $this->updateStmt->execute( $values );      }        function selectStmt() {          return $this->selectStmt;      }        function selectAllStmt() {          return $this->selectAllStmt;      }    }    //client代碼    $venue = new venue();  $venue->setName("XXXXXXX");  //插入一條資料  $mapper = new VenueMapper();  $mapper->insert($venue);  //擷取剛插入的資料  $venueInfo = $mapper->find($venue->getId());  //修改資料   $venue->setName('OOOOOOOOOOO');  $mapper->update($venue);  ?>

代碼省略了一些輔助類,保留最主要的領域對象和資料對應器。資料對應器模式最強大的地方在於消除了領域層和資料庫操作之間的耦合。Mapper對象在幕後運作,可以應用於各種對象關係映射。而與之帶來的是需要建立大量具體的映射器類。不過現在架構都可以通過程式自動產生了。

四、使用時機

使用資料庫映射器的主要是資料庫方案和物件模型需要彼此獨立演變的時候。最常見的當然是和領域模式一起使用。資料對應器無論是在設計階段、開發階段,還是測試階段,在領域模型上操作時可以不考慮資料庫。領域對象對資料庫的結構一無所知,因為所有這些對應關係都由資料對應器完成。

當然,資料對應器引入了新的層次,因此使用這些模式的前提條件是商務邏輯的複雜性,如果很簡單,那就沒必要了。

如果沒有領域模型,我不會選用資料對應器。但是沒有資料對應器時,能使用領域模型嗎?如果領域模型簡單,且資料庫受領域模型開發人員的控制,則領域對象用活動記錄直接存取資料庫也是合理的。

不必建立完全意義上的資料庫映射層。建立這樣的資料對應器很複雜。大多數情況下,建議使用開源的資料庫映射層而不是自己動手建立

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.