DAO(Data Access Object) 應該是最基礎且最廣泛運用的一個設計模式了。其適用的情境為:
- 程式需要利用別的媒介來儲存或取得資料
- 程式需要能移植到其它儲存媒介
- 各個儲存媒介之間使用的存取方式不同
- 希望程式不會受到儲存媒介的變換而修改
DAO 通常看到的範例都會與另外一個物件搭配使用,稱之為 Data Transfer Object 。但是這兩者之間並不一定要綁在一起用。DAO 的重點在提供一個統一的資料存取方式,而回傳的資料可以用 DTO 表示,但也可以用其它方式(例如陣列)來實作。這邊以一個會員系統為例,如何以 DAO 模式來提供會員相關資料的存取。
首先需要設計一個 Interface 來提供資料的存取,這邊為了簡化程式數量,還是按照常見作法搭配 DTO 來使用:
1 2 3 4 5 6 7 |
interface IUserDAO { public function create(); public function findById($id); public function updateUser(User $user); public function deleteUser(User $user); } |
有了這個 Interface 之後,我們就可以實作兩種 DAO 分別對應到記憶體與資料庫這兩種不同的儲存媒介:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class MemoryUserDAO implements IUserDAO { public function create() { // TODO: Implement create() method. } public function findById($id) { // TODO: Implement findById() method. } public function updateUser(User $user) { // TODO: Implement save() method. } public function deleteUser(User $user) { // TODO: Implement delete() method. } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
class MySQLUserDAO implements IUserDAO { public function create() { // TODO: Implement create() method. } public function findById($id) { // TODO: Implement findById() method. } public function updateUser(User $user) { // TODO: Implement save() method. } public function deleteUser(User $user) { // TODO: Implement delete() method. } } |
因為偷懶的關係所以這兩個 DAO 長的根本一模一樣。但是實際應用的時候 MemoryUserDAO 應該只會帶有一些陣列操作之類的程式碼,而 MySQLUserDAO 裡面可能會有 PDO 及 SQL 語法,或是利用 ORM 來存取資料。
使用的時候我們就可以不用管 DAO 實際對應的媒介,只需要呼叫其界面提供的方法就可以了:
1 2 3 4 5 6 7 8 |
public function isValidUser(User $currentUser, IUserDAO $userDAO) { $actualUser = $userDAO->findById($currentUser->getId()); if ($currentUser->getPasswordHash() === $actualUser->getPasswordHash()) { return true; } return false; } |
另外,當需要支援更多種類的儲存媒介,你可以很容易的實作更多的 DAO 來處理。而且完全不用修改現有的程式碼。這同時也提供了程式良好的可測性,我們只需要實作一個 DAO 來控制回傳的資料,就能測試各種資料組合的單元測試了。
總結一下優點:
- 將特定媒介的 API 使用方式隔離在 DAO 內,易於維護
- 易於新增或抽換儲存媒介
- 降低程式的複雜度
- 提昇可測性
參考資料:
http://www.oracle.com/technetwork/java/dataaccessobject-138824.html
http://www.adam-bien.com/roller/abien/entry/value_object_vs_data_transfer
http://martinfowler.com/eaaCatalog/dataTransferObject.html