一、强烈推荐使用接口(最佳实践)
✅ 为什么需要接口?
-
解耦与抽象
// 接口定义契约 public interface UserDao { User findById(int id); void save(User user); void update(User user); void delete(int id); } // 具体实现(可随时替换) @Repository public class UserDaoJdbcImpl implements UserDao { // JDBC实现... } @Repository public class UserDaoJpaImpl implements UserDao { // JPA实现... }
-
便于单元测试
// Service层测试(不依赖真实DAO) @ExtendWith(MockitoExtension.class) class UserServiceTest { @Mock private UserDao userDao; // 模拟接口 @InjectMocks private UserService userService; @Test void testGetUser() { when(userDao.findById(1)).thenReturn(new User(1, "Tom")); User user = userService.getUser(1); assertEquals("Tom", user.getName()); } }
-
多数据源支持
// 根据配置切换实现 @Configuration public class DaoConfig { @Bean @Profile("mysql") public UserDao userDaoMysql() { return new UserDaoMySqlImpl(); } @Bean @Profile("oracle") public UserDao userDaoOracle() { return new UserDaoOracleImpl(); } }
-
框架集成优势
- Spring的依赖注入基于接口类型
- MyBatis的Mapper本身就是接口
二、直接写实现类的场景(谨慎使用)
⚠️ 仅适用于:
-
微型项目/原型验证
// 直接实现类(不推荐用于正式项目) @Repository public class UserDao { private static final Map<Integer, User> mockDB = new HashMap<>(); public User findById(int id) { return mockDB.get(id); } // 其他CRUD方法... }
-
确定永不更换实现
- 比如始终使用Hibernate且永不切换数据库
三、架构对比
方案1:接口+实现类(推荐)
src
├── main
│ ├── java
│ │ └── com/example/dao
│ │ ├── UserDao.java // 接口
│ │ └── impl
│ │ └── UserDaoJdbcImpl.java // 实现
│ └── resources
方案2:直接实现类(简单场景)
src
├── main
│ ├── java
│ │ └── com/example/dao
│ │ └── UserDao.java // 直接实现类
│ └── resources
四、实际项目中的妥协方案
1. 使用Spring Data JPA(接口自动实现)
// 无需写实现类
public interface UserRepository extends JpaRepository<User, Integer> {
// 自动生成CRUD实现
User findByName(String name);
}
2. MyBatis方案(接口+XML映射)
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User findById(int id);
// XML映射文件实现复杂SQL
void updateUser(User user);
}
<!-- UserMapper.xml -->
<update id="updateUser">
UPDATE users SET name=#{name} WHERE id=#{id}
</update>
五、决策指南
情况 | 推荐方案 | 示例场景 |
---|---|---|
企业级项目 | 必须使用接口 | 电商系统、金融系统 |
需要单元测试 | 必须使用接口 | TDD开发流程 |
可能切换数据源/ORM框架 | 必须使用接口 | 从JDBC迁移到MyBatis |
使用Spring Data JPA/MyBatis | 框架强制使用接口 | 现代JavaWeb项目 |
学生作业/简单Demo | 可直接写实现类 | 课程实验项目 |
一次性脚本 | 可直接写实现类 | 数据迁移工具 |
六、最佳实践示例
// 1. 定义接口
public interface BookDao {
Book getByIsbn(String isbn);
List<Book> findAll();
void save(Book book);
void update(Book book);
void delete(String isbn);
}
// 2. JDBC实现
@Repository
public class BookDaoJdbcImpl implements BookDao {
private final JdbcTemplate jdbcTemplate;
@Autowired
public BookDaoJdbcImpl(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
@Override
public Book getByIsbn(String isbn) {
String sql = "SELECT * FROM books WHERE isbn = ?";
return jdbcTemplate.queryForObject(sql, new BookRowMapper(), isbn);
}
// 其他方法实现...
}
// 3. Service层通过接口调用
@Service
public class BookService {
private final BookDao bookDao; // 依赖接口
@Autowired
public BookService(BookDao bookDao) {
this.bookDao = bookDao;
}
public Book getBookDetails(String isbn) {
return bookDao.getByIsbn(isbn);
}
}
总结建议:
- 正式项目必须使用接口 - 这是Java企业开发的行业标准
- 接口命名去掉Impl后缀 - 如
UserDao
接口,UserDaoJdbcImpl
实现 - 结合Spring使用更高效 - 利用
@Repository
+ 依赖注入 - 即使简单项目也建议用接口 - 培养良好设计习惯
- 现代框架已强制使用接口 - 如Spring Data JPA、MyBatis
📌 核心原则:面向接口编程是JavaEE的核心设计思想,它能显著提高代码的灵活性、可测试性和可维护性,是专业开发的必备实践。