資料對應 Data Mapper 資料對應,這是一種常用於處理物件導向與資料庫資料的模式,與 Repository
不同,Data Mapper
主要處理的事單個物件本身,而 Repository
處理的是物件的集合。這次實作舉個例子,你在買大頭菜之前,需要有一個草圖去評估你要前往哪些島上買大頭菜,決定好之後再開始行動。
UML
實作 首先我們要先把大頭菜給實作出來,這次因為大頭菜可以來自不同的島嶼,因此多了島嶼名稱,以及透過 new self(...)
的方式來回傳新的大頭菜物件。
Turnips.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 38 39 40 41 42 43 44 45 46 47 48 class Turnips { protected $island ; protected $price ; protected $count ; public function __construct (string $island , int $price , int $count ) { $this ->island = $island ; $this ->price = $price ; $this ->count = $count ; } public static function fromIsland (array $island ): Turnips { return new self ( $island ['island' ], $island ['price' ], $island ['count' ] ); } }
Turnips
你可以理解為 ORM 物件,所以接下來要製作所謂的草稿(StorageAdapter)以及對應的中間層(TurnipsMapper),把草稿寫好之後,丟進去中間層(TurnipsMapper)就可以對應到每個大頭菜物件。
【補充說明】 物件關聯對映(英語:Object Relational Mapping,簡稱ORM,或O/RM,或O/R mapping),是一種程式設計技術,用於實現物件導向程式語言裡不同類型系統的資料之間的轉換。從效果上說,它其實是建立了一個可在程式語言裡使用的「虛擬物件資料庫」。如今已有很多免費和付費的ORM產品,而有些程式設計師更傾向於建立自己的ORM工具。 資料來源:維基百科
TurnipsMapper.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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 class TurnipsMapper { protected $adapter ; public function __construct (StorageAdapter $storage ) { $this ->adapter = $storage ; } public function findById (int $id ): Turnips { $result = $this ->adapter->findById ($id ); if ($result === null ) { throw new \InvalidArgumentException ("找不到 ID 為「 $id 」的島嶼。" ); } return $this ->mapRowToTurnips ($result ); } public function findByIsland (string $island ): Turnips { $result = $this ->adapter->findByIsland ($island ); if ($result === null ) { throw new \InvalidArgumentException ("找不到名稱為「 $island 」的島嶼。" ); } return $this ->mapRowToTurnips ($result ); } protected function mapRowToTurnips (array $row ): Turnips { return Turnips ::fromIsland ($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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 class StorageAdapter { protected $data = []; public function __construct (array $data ) { $this ->data = $data ; } public function findById (int $id ) { if (isset ($this ->data[$id ])) { return $this ->data[$id ]; } return null ; } public function findByIsland (string $island ) { $key = array_search ($island , array_column ($this ->data, 'island' )); if (false !== $key ) { return $this ->data[$key ]; } return null ; } }
測試 撰寫完資料對應器之後,接下來要寫些測試來驗證資料對應器是否能夠正確的運作,預期是繪製好草圖,把草圖塞入對應器之後,獲得相對應的大頭菜物件,所以會有以下幾個重點項目需要測試:
測試塞入大頭菜列,並且取得 id 為 1 的大頭菜。
測試塞入空的大頭菜列,並且成功獲得例外錯誤。
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 38 39 40 41 42 43 44 class DataMapperTest extends TestCase { public function test_can_map_turnips_from_storage ( ) { $storage = new StorageAdapter ( array ( 1 => array ( 'island' => 'kantai' , 'price' => 100 , 'count' => 40 , ), ), ); $mapper = new TurnipsMapper ($storage ); $turnips = $mapper ->findById (1 ); $this ->assertInstanceOf (Turnips ::class , $turnips ); } public function test_will_not_map_invalid_data ( ) { $this ->expectException (\InvalidArgumentException ::class ); $storage = new StorageAdapter ([]); $mapper = new TurnipsMapper ($storage ); $mapper ->findById (1 ); } }
最後測試的執行結果會獲得如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 PHPUnit Pretty Result Printer 0.28.0 by Codedungeon and contributors. ==> Configuration: ~/php-design-pattern/vendor/codedungeon/phpunit-result-printer/src/phpunit-printer.yml PHPUnit 9.2.6 by Sebastian Bergmann and contributors. ==> AbstractFactoryTest ✔ ✔ ✔ ✔ ==> BuilderPatternTest ✔ ✔ ✔ ✔ ==> FactoryMethodTest ✔ ✔ ✔ ✔ ==> PoolPatternTest ✔ ✔ ==> PrototypePatternTest ✔ ✔ ==> SimpleFactoryTest ✔ ✔ ✔ ✔ ==> SingletonPatternTest ✔ ==> StaticFactoryTest ✔ ✔ ✔ ✔ ✔ ==> AdapterPatternTest ✔ ✔ ==> BridgePatternTest ✔ ✔ ✔ ==> CompositePatternTest ✔ ✔ ✔ ==> DataMapperTest ✔ ✔ Time: 00:00.050, Memory: 6.00 MB OK (36 tests, 83 assertions)
完整程式碼 設計模式不難,找回快樂而已,以大頭菜為例。
參考文獻