我的项目经理要我使用DAO / DTO对象来访问和检索数据库中的数据。 Project是用Java SE编写的,不使用任何框架或ORM。他的论点是使代码更易于测试并改进代码设计。它有意义吗?
初始化DAO对象怎么样?它应该在创建具有DAO字段的类的实例时初始化:
private PersonDao personDao = new PersonDaoImpl();
或者在必要时初始化?
public class A {
private PersonDao person;
public List<Person> findAll() {
person = new PersonDaoImpl();
return person.getAll();
}
}
它允许轻松地模拟这个界面,但它是否适合DAO模式使用惯例?
答案 0 :(得分:2)
数据访问对象基本上是一个对象或接口,提供对底层数据库或任何其他持久性存储的访问。
该定义来自:http://en.wikipedia.org/wiki/Data_access_object
也许一个简单的例子可以帮助你理解这个概念:
我们假设我们有一个代表员工的实体:
public class Employee {
private int id;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
员工实体将持久保存到数据库中的相应Employee表中。处理操作员工实体所需的数据库操作的简单DAO接口如下:
interface EmployeeDAO {
List<Employee> findAll();
List<Employee> findById();
List<Employee> findByName();
boolean insertEmployee(Employee employee);
boolean updateEmployee(Employee employee);
boolean deleteEmployee(Employee employee);
}
接下来,我们必须为该接口提供一个具体的实现来处理SQL服务器,另一个用于处理平面文件等...
希望有所帮助
答案 1 :(得分:0)
您使用它的方式仍与您的A级紧密结合。
您应该使用构造函数或setter将DAO作为依赖项提供。可能最好的方法是使用某种控制反转(例如依赖注入框架)。
你的A班应该是这样的:
public class A {
private PersonDao personDao;
// possibly an @Inject annotation
public A(PersonDao personDao) {
this.personDao = personDao;
}
public List<Person> findAll() {
return personDao.getAll();
}
}
实际上,我认为这是一种反模式。这取决于你将如何使用你的A班。 如果它包含不同的业务逻辑 - 很好。 如果它只是调用DAO(我不喜欢这个名字,也许使用Repository;))那么它只是不必要的抽象层。
另一件事 - 你提到了DTO。所以Person类在这种情况下只是一个DTO?在这里,我们可以有另一个反模式。例如,如果您需要将业务对象转换为屏幕上可见的内容,则DTO很好。或者将持久模型与业务模型分开。
我想说的是:不要让Person类只是一个数据结构。给它一些行为。
答案 2 :(得分:0)
DAO模式不是“企业”模式。它主要出现在“企业”应用程序中,但您绝对可以在仅用SE编写的应用程序中使用。
这不是因为你正在编写一个你不需要测试的SE应用程序,所以你的代码使用DAO模式和IOC会更加可测试,而不是在应用程序中使用直接JDBC。
使用DAO实现类的方式存在问题,因为类A和DAO实现之间的紧密耦合导致无法正确测试类。你最好使用IOC模式以及像Guice或Dagger这样的框架(两者都考虑到了SE)。
有关代码示例,请查看slnowak的答案。
答案 3 :(得分:0)
为了最大限度地发挥可测试性和关注点分离的好处,您应该引入控制反转(IoC)的概念。将IoC应用于对象生命周期管理时,使用术语依赖注入。这意味着你的A类应该完全不知道在什么时候实例化实现。
为了实现这一目标,您需要一个额外的组件来引导您的应用程序并使用正确的实现注入所有类。
您可以像这样设置依赖接收类(setter注入,也可以使用构造函数)
public class PersonServiceImpl implements PersonService {
private PersonDao personDao;
public List<Person> findAll() {
return personDao.getAll();
}
public setPersonDaoA(PersonDao personDao) {
this.personDao = personDao;
}
}
进行依赖注入的组件:
public class ApplicationContext {
private PersonService personService;
private PersonDao personDao ;
public PersonService getPersonService() {
if (personService == null) {
personService= new PersonServiceIml();
personService.setPersonDao(getPersonDao());
}
return personService;
}
public PersonDao getPersonDao() {
if (personDao == null) {
personDao = new PersonDaoIml();
}
return personDao ;
}
}
然后应用程序启动会涉及到:
public class Main {
public static void main(String[] args) {
ApplicationContext ctx = new ApplicationContext();
PersonService personService = ctx.getPersonService();
personService.findAll();
}
}
如您所见,ApplicationContext封装了关于:
的knowlegdePersonServiceImpl类现在完全可测试,并且已经从中提取了有关对象生命周期管理的所有问题。
在现实生活中,这通常是使用像Spring或CDI这样的框架(最近变得越来越流行)。但在你的情况下,从上面的方法开始可能是一个很好的第一步。它将获得项目经理提到的直接好处,而不会产生引入Spring的开销,可能也会更改构建,并且必须了解其工作原理(例如,使用XML上下文,源代码上下文和/或注释)。
稍后介绍Spring将很容易,因为所有类都已经为依赖注入做好了准备。请记住,您的工厂(我的示例中的ApplicationContext)不应承担任何额外的责任,如配置管理。
还要记住,上面的ApplicationContext示例不是单例。您自己应该确保在应用程序启动时只创建一个实例,并且所有注入都由它处理。创建重复的实例可能会导致混乱的错误。