数据映射模式 目的 数据映射器,是一个数据访问层,它在持久数据存储(通常是关系数据库)和内存数据(领域层)之间执行数据的双向传输。该模式的目标是使内存中的数据和持久层中的数据存储相互独立,并保持数据映射器本身的独立性。领域层由一个或多个映射器(或数据访问对象)组成,并执行数据传输。数据映射器的实现方式各不相同。通用映射器将处理许多不同领域的实体类型,专用映射器只处理一个或少量实体类型。
与活动记录(Active Record)模式不同,数据映射模式的关键点在于:数据模型遵循单一职责原则。
使用场景
数据库对象关系映射(ORM):Doctrine2 使用名为 EntityRepository
的 DAO
UML 类图
代码 User.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <?php declare (strict_types = 1 );namespace DesignPatterns \Extend \DataMapper ;class User { private string $username; private string $email; public static function fromState (array $state ): User { return new self ( $state['username' ], $state['email' ] ); } public function __construct (string $username, string $email ) { $this ->username = $username; $this ->email = $email; } public function getUsername ( ): string { return $this ->username; } public function getEmail ( ): string { return $this ->email; } }
UserMapper.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <?php declare (strict_types = 1 );namespace DesignPatterns \Extend \DataMapper ;use InvalidArgumentException ;class UserMapper { private StorageAdapter $adapter; public function __construct (StorageAdapter $storage ) { $this ->adapter = $storage; } public function findById (int $id ): User { $result = $this ->adapter->find($id); if ($result === null ) { throw new InvalidArgumentException ("User #$id not found" ); } return $this ->mapRowToUser($result); } private function mapRowToUser (array $row ): User { return User::fromState($row); } }
StorageAdapter.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <?php declare (strict_types = 1 );namespace DesignPatterns \Extend \DataMapper ;class StorageAdapter { private array $data = []; public function __construct (array $data ) { $this ->data = $data; } public function find (int $id ) { if (isset ($this ->data[$id])) { return $this ->data[$id]; } return null ; } }
测试 Tests/DataMapperTest.php 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 <?php declare (strict_types = 1 );namespace DesignPatterns \Extend \DataMapper \Tests ;use InvalidArgumentException ;use DesignPatterns \Extend \DataMapper \StorageAdapter ;use DesignPatterns \Extend \DataMapper \User ;use DesignPatterns \Extend \DataMapper \UserMapper ;use PHPUnit \Framework \TestCase ;class DataMapperTest extends TestCase { public function testCanMapUserFromStorage ( ) { $storage = new StorageAdapter([1 => ['username' => 'domnikl' , 'email' => 'liebler.dominik@gmail.com' ]]); $mapper = new UserMapper($storage); $user = $mapper->findById(1 ); $this ->assertInstanceOf(User::class, $user); } public function testWillNotMapInvalidData ( ) { $this ->expectException(InvalidArgumentException ::class); $storage = new StorageAdapter([]); $mapper = new UserMapper($storage); $mapper->findById(1 ); } }