适配器模式(Adapter)

目的

将一个类的接口转换成可应用的兼容接口。适配器使原本由于接口不兼容而不能一起工作的那些类可以一起工作。

例子

  • 客户端数据库适配器
  • 使用多个不同的网络服务和适配器来规范数据使得出结果是相同的

UML 图

oHBRHvqDHI.png

代码

你也可以在 GitHub 上查看代码

BookInterface.php

  1. <?php
  2. namespace DesignPatterns\Structural\Adapter;
  3. interface BookInterface
  4. {
  5. public function turnPage();
  6. public function open();
  7. public function getPage(): int;
  8. }

Book.php

  1. <?php
  2. namespace DesignPatterns\Structural\Adapter;
  3. class Book implements BookInterface
  4. {
  5. /**
  6. * @var int
  7. */
  8. private $page;
  9. public function open()
  10. {
  11. $this->page = 1;
  12. }
  13. public function turnPage()
  14. {
  15. $this->page++;
  16. }
  17. public function getPage(): int
  18. {
  19. return $this->page;
  20. }
  21. }

EBookAdapter.php

  1. <?php
  2. namespace DesignPatterns\Structural\Adapter;
  3. /**
  4. * 这里是一个适配器. 注意他实现了 BookInterface,
  5. * 因此你不必去更改客户端代码当使用 Book
  6. */
  7. class EBookAdapter implements BookInterface
  8. {
  9. /**
  10. * @var EBookInterface
  11. */
  12. protected $eBook;
  13. /**
  14. * @param EBookInterface $eBook
  15. */
  16. public function __construct(EBookInterface $eBook)
  17. {
  18. $this->eBook = $eBook;
  19. }
  20. /**
  21. * 这个类使接口进行适当的转换.
  22. */
  23. public function open()
  24. {
  25. $this->eBook->unlock();
  26. }
  27. public function turnPage()
  28. {
  29. $this->eBook->pressNext();
  30. }
  31. /**
  32. * 注意这里适配器的行为: EBookInterface::getPage() 将返回两个整型,除了 BookInterface
  33. * 仅支持获得当前页,所以我们这里适配这个行为
  34. *
  35. * @return int
  36. */
  37. public function getPage(): int
  38. {
  39. return $this->eBook->getPage()[0];
  40. }
  41. }

EBookInterface.php

  1. <?php
  2. namespace DesignPatterns\Structural\Adapter;
  3. interface EBookInterface
  4. {
  5. public function unlock();
  6. public function pressNext();
  7. /**
  8. * 返回当前页和总页数,像 [10, 100] 是总页数100中的第10页。
  9. *
  10. * @return int[]
  11. */
  12. public function getPage(): array;
  13. }

Kindle.php

  1. <?php
  2. namespace DesignPatterns\Structural\Adapter;
  3. /**
  4. * 这里是适配过的类. 在生产代码中, 这可能是来自另一个包的类,一些供应商提供的代码。
  5. * 注意它使用了另一种命名方案并用另一种方式实现了类似的操作
  6. */
  7. class Kindle implements EBookInterface
  8. {
  9. /**
  10. * @var int
  11. */
  12. private $page = 1;
  13. /**
  14. * @var int
  15. */
  16. private $totalPages = 100;
  17. public function pressNext()
  18. {
  19. $this->page++;
  20. }
  21. public function unlock()
  22. {
  23. }
  24. /**
  25. * 返回当前页和总页数,像 [10, 100] 是总页数100中的第10页。
  26. *
  27. * @return int[]
  28. */
  29. public function getPage(): array
  30. {
  31. return [$this->page, $this->totalPages];
  32. }
  33. }

测试

Tests/AdapterTest.php

  1. <?php
  2. namespace DesignPatterns\Structural\Adapter\Tests;
  3. use DesignPatterns\Structural\Adapter\Book;
  4. use DesignPatterns\Structural\Adapter\EBookAdapter;
  5. use DesignPatterns\Structural\Adapter\Kindle;
  6. use PHPUnit\Framework\TestCase;
  7. class AdapterTest extends TestCase
  8. {
  9. public function testCanTurnPageOnBook()
  10. {
  11. $book = new Book();
  12. $book->open();
  13. $book->turnPage();
  14. $this->assertEquals(2, $book->getPage());
  15. }
  16. public function testCanTurnPageOnKindleLikeInANormalBook()
  17. {
  18. $kindle = new Kindle();
  19. $book = new EBookAdapter($kindle);
  20. $book->open();
  21. $book->turnPage();
  22. $this->assertEquals(2, $book->getPage());
  23. }
  24. }