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》