数据映射模式(Data Mapper)

目标

数据映射器是一种数据访问层,它执行持久性数据存储(通常是关系数据库)和内存数据表示(域层)之间的数据双向传输。 该模式的目标是保持内存表示和持久数据存储相互独立,并保持数据映射器本身。 该层由一个或多个映射器(或数据访问对象)组成,执行数据传输。 映射器实现的范围有所不同。 通用映射器将处理许多不同的域实体类型,专用映射器将处理一个或几个。

这种模式的关键点在于,与活动记录模式不同,数据模型遵循单一责任原则。

例子

数据库对象关系映射器( ORM ):Doctrine2 使用的 DAO,名字叫做 “ EntityRepository ”。

UML 图

WHNWAjWM3i.png

代码

你能在 GitHub 上面找到这些代码

User.php

  1. <?php
  2. namespace DesignPatterns\Structural\DataMapper;
  3. class User
  4. {
  5. /**
  6. * @var string
  7. */
  8. private $username;
  9. /**
  10. * @var string
  11. */
  12. private $email;
  13. public static function fromState(array $state): User
  14. {
  15. // 在你访问的时候验证状态
  16. return new self(
  17. $state['username'],
  18. $state['email']
  19. );
  20. }
  21. public function __construct(string $username, string $email)
  22. {
  23. // 先验证参数再设置他们
  24. $this->username = $username;
  25. $this->email = $email;
  26. }
  27. /**
  28. * @return string
  29. */
  30. public function getUsername()
  31. {
  32. return $this->username;
  33. }
  34. /**
  35. * @return string
  36. */
  37. public function getEmail()
  38. {
  39. return $this->email;
  40. }
  41. }

UserMapper.php

  1. <?php
  2. namespace DesignPatterns\Structural\DataMapper;
  3. class UserMapper
  4. {
  5. /**
  6. * @var StorageAdapter
  7. */
  8. private $adapter;
  9. /**
  10. * @param StorageAdapter $storage
  11. */
  12. public function __construct(StorageAdapter $storage)
  13. {
  14. $this->adapter = $storage;
  15. }
  16. /**
  17. * 根据 id 从存储器中找到用户,并返回一个用户对象
  18. * 在内存中,通常这种逻辑将使用 Repository 模式来实现
  19. * 然而,重要的部分是在下面的 mapRowToUser() 中,它将从中创建一个业务对象
  20. * 从存储中获取的数据
  21. *
  22. * @param int $id
  23. *
  24. * @return User
  25. */
  26. public function findById(int $id): User
  27. {
  28. $result = $this->adapter->find($id);
  29. if ($result === null) {
  30. throw new \InvalidArgumentException("User #$id not found");
  31. }
  32. return $this->mapRowToUser($result);
  33. }
  34. private function mapRowToUser(array $row): User
  35. {
  36. return User::fromState($row);
  37. }
  38. }

StorageAdapter.php

  1. <?php
  2. namespace DesignPatterns\Structural\DataMapper;
  3. class StorageAdapter
  4. {
  5. /**
  6. * @var array
  7. */
  8. private $data = [];
  9. public function __construct(array $data)
  10. {
  11. $this->data = $data;
  12. }
  13. /**
  14. * @param int $id
  15. *
  16. * @return array|null
  17. */
  18. public function find(int $id)
  19. {
  20. if (isset($this->data[$id])) {
  21. return $this->data[$id];
  22. }
  23. return null;
  24. }
  25. }

测试

Tests/DataMapperTest.php

  1. <?php
  2. namespace DesignPatterns\Structural\DataMapper\Tests;
  3. use DesignPatterns\Structural\DataMapper\StorageAdapter;
  4. use DesignPatterns\Structural\DataMapper\User;
  5. use DesignPatterns\Structural\DataMapper\UserMapper;
  6. use PHPUnit\Framework\TestCase;
  7. class DataMapperTest extends TestCase
  8. {
  9. public function testCanMapUserFromStorage()
  10. {
  11. $storage = new StorageAdapter([1 => ['username' => 'domnikl', 'email' => 'liebler.dominik@gmail.com']]);
  12. $mapper = new UserMapper($storage);
  13. $user = $mapper->findById(1);
  14. $this->assertInstanceOf(User::class, $user);
  15. }
  16. /**
  17. * @expectedException \InvalidArgumentException
  18. */
  19. public function testWillNotMapInvalidData()
  20. {
  21. $storage = new StorageAdapter([]);
  22. $mapper = new UserMapper($storage);
  23. $mapper->findById(1);
  24. }
  25. }