DAO 和 Repository 的區別
- 1.概述
- 2.DAO 模式
- 2.1 User
- 2.2 UserDao
- 2.3 UserDaoImpl
- 3.Repository 模式
- 3.1 UserRepository
- 3.2 UserRepositoryImpl
- 4.具有多個 DAO 的 Repository 模式
- 4.1 Tweet
- 4.2 TweetDao 和 TweetDaoImpl
- 4.3 增強 User 域
- 4.4 UserRepositoryImpl
- 5.比較兩種模式
- 6.結論
1.概述
通常,Repository 和 DAO 的實現被認為是可互換的,特別是在 以數據為中心 的應用程序中。這造成了對它們差異的混淆。
在本文中,我們將討論 DAO 和 Repository 模式之間的差異。
2.DAO 模式
數據訪問對象(Data Access Object
,DAO
)模式是數據持久性的抽象,被認為更接近于底層存儲,而底層存儲通常以表為中心。因此,在許多情況下,我們的 DAO 與數據庫表匹配,允許更直接的方式從存儲中 發送/檢索 數據,從而隱藏丑陋的查詢。
讓我們來看看 DAO 模式的簡單實現。
2.1 User
首先,讓我們創建一個基本的 User 域類。
public class User {private Long id;private String userName;private String firstName;private String email;// getters and setters
}
2.2 UserDao
然后,我們將創建 UserDao 接口,該接口為 User 域提供簡單的 CRUD 操作。
public interface UserDao {void create(User user);User read(Long id);void update(User user);void delete(String userName);
}
2.3 UserDaoImpl
最后,我們將創建實現 UserDao 接口的 UserDaoImpl 子類。
public class UserDaoImpl implements UserDao {private final EntityManager entityManager;@Overridepublic void create(User user) {entityManager.persist(user);}@Overridepublic User read(long id) {return entityManager.find(User.class, id);}// ...
}
在這里,為了簡單起見,我們使用 JPA EntityManager 接口與底層存儲交互,并為用戶域提供數據訪問機制。
3.Repository 模式
根據 Eric Evans 的《領域驅動設計》:Repository 是一種封裝存儲、檢索和搜索行為的機制,它模擬對象的集合。
同樣,根據《企業應用架構模式》,它使用類似于集合的接口訪問領域對象,在 領域層 和 數據映射層 之間進行調解。換句話說,Repository 也處理數據并隱藏查詢,與 DAO 類似。不過,它位于更高層次,更接近應用程序的業務邏輯。
因此,Repository 可以使用 DAO 從數據庫獲取數據并填充域對象。或者,它可以準備域對象中的數據,并使用 DAO 將其發送到存儲系統以實現持久性。
讓我們看看 User 域的 Repository 模式的簡單實現。
3.1 UserRepository
首先,讓我們創建用戶存儲庫接口。
public interface UserRepository {User get(Long id);void add(User user);void update(User user);void remove(User user);
}
在這里,我們添加了一些常用方法,如 get
、add
、update
和 remove
,以處理對象集合。
3.2 UserRepositoryImpl
然后,我們將創建 UserRepositoryImpl 類,提供 UserRepository 接口的實現。
public class UserRepositoryImpl implements UserRepository {private UserDaoImpl userDaoImpl;@Overridepublic User get(Long id) {User user = userDaoImpl.read(id);return user;}@Overridepublic void add(User user) {userDaoImpl.create(user);}// ...
}
在這里,我們使用 UserDaoImpl 從數據庫中 發送/檢索 數據。
到目前為止,我們可以說 DAO 和 Repository 的實現看起來非常相似,因為 User 類是一個 Anemic Domain。而且,Repository 只是數據訪問層(DAO)的另一層。
🚀 只包含數據,不包含業務邏輯的類,就叫作 貧血模型(Anemic Domain Model)。貧血模型將數據與操作分離,破壞了面向對象的封裝特性,是一種典型的面向過程的編程風格。
但是,DAO 似乎是 訪問數據 的完美候選者,Repository 是 實現業務用例 的理想方式。
4.具有多個 DAO 的 Repository 模式
為了清楚地理解最后一句話,讓我們加強 User 域來處理業務用例。
想象一下,我們想通過聚合一個用戶的 Twitter 推文、Facebook 帖子等,為他準備一份社交媒體檔案。
4.1 Tweet
首先,我們將創建具有一些保存推文信息的屬性的 Tweet 類:
public class Tweet {private String email;private String tweetText; private Date dateCreated;// getters and setters
}
4.2 TweetDao 和 TweetDaoImpl
然后,與 UserDao 類似,我們將創建允許獲取推文的 TweetDao 界面:
public interface TweetDao {List<Tweet> fetchTweets(String email);
}
同樣,我們將創建 TweetDaoImpl 類,該類提供 fetchTweets 方法的實現:
public class TweetDaoImpl implements TweetDao {@Overridepublic List<Tweet> fetchTweets(String email) {List<Tweet> tweets = new ArrayList<Tweet>();//call Twitter API and prepare Tweet objectreturn tweets;}
}
在這里,我們將調用 Twitter API,使用用戶的電子郵件獲取其所有推文。
因此,在這種情況下,DAO 提供了一種使用第三方 API 的數據訪問機制。
4.3 增強 User 域
最后,讓我們創建 User 類的 UserSocialMedia 子類,以保留 Tweet 對象的列表。
public class UserSocialMedia extends User {private List<Tweet> tweets;// getters and setters
}
在這里,我們的 UserSocialMedia 是一個復雜的域,其中包含 User 域的屬性。
4.4 UserRepositoryImpl
現在,我們將升級我們的 UserRepositoryImpl 類,以提供 User 域對象以及推文列表。
public class UserRepositoryImpl implements UserRepository {private UserDaoImpl userDaoImpl;private TweetDaoImpl tweetDaoImpl;@Overridepublic User get(Long id) {UserSocialMedia user = (UserSocialMedia) userDaoImpl.read(id);List<Tweet> tweets = tweetDaoImpl.fetchTweets(user.getEmail());user.setTweets(tweets);return user;}
}
在這里,UserRepositoryImpl 使用 UserDaoImpl 提取用戶數據,并使用 TweetDaoImpl 提取用戶的推文。
然后,它聚合兩組信息,并提供 UserSocialMedia 類的域對象,該對象對于我們的業務用例非常方便。因此,存儲庫依賴于 DAO 來訪問來自各種源的數據。
同樣,我們可以增強我們的用戶域以保留 Facebook 帖子列表。
5.比較兩種模式
現在我們已經看到了 DAO 和 Repository 模式的細微差別,讓我們總結一下它們的區別:
- DAO 是數據持久性的抽象。但是,Repository 是對象集合的抽象。
- DAO 是一個較 低 級別的概念,更接近 存儲系統。但是,Repository 是一個更 高 層次的概念,更接近 域對象。
- DAO 作為 數據映射/訪問層 工作,隱藏丑陋的查詢。但是,Repository 是 域 和 數據訪問層 之間的一層,隱藏了整理數據和準備域對象的復雜性。
- DAO 不能使用 Repository 實現。但是,Repository 可以使用 DAO 來訪問底層存儲。
而且,如果我們有一個貧血域,Repository 將只是一個 DAO。
此外,Repository 模式鼓勵域驅動的設計,也為非技術團隊成員提供了對數據結構的輕松理解。
- 職責不同:Repository 負責數據訪問的邏輯管理,封裝與數據庫的交互操作,包括數據的增刪改查等。Repository 的設計目標是提供一個統一的接口,將數據訪問邏輯與業務邏輯分離,使得業務邏輯更加清晰和可維護。DAO 更關注于數據訪問的實現細節,與具體的數據庫交互,實現數據的存儲和檢索等功能。
- 抽象層次不同:Repository 提供一種更加抽象的數據訪問方式,將數據訪問的邏輯從業務邏輯中分離出來,使得業務邏輯與數據訪問無關,降低代碼耦合度,提高可維護性和可擴展性。Repository 定義一組通用的接口和方法,供業務模塊調用。DAO 更接近于具體的數據訪問實現,直接與數據庫交互,實現數據的存儲和檢索等功能。DAO 的設計目標是提供一組具體的數據訪問方法,以滿足業務模塊的具體需求。
6.結論
在本文中,我們探討了 DAO 和 Repository 模式之間的差異。
首先,我們研究了 DAO 模式的基本實現。然后,我們看到了使用 Repository 模式的類似實現。
最后,我們研究了一個利用多個 DAO 的 Repository,它增強了域解決業務用例的能力。
因此,我們可以得出結論,當應用程序從以數據為中心轉向面向業務時,Repository 模式被證明是一種更好的方法。
🚀 譯自《DAO vs Repository Patterns》