토비의 스프링 공부 후 정리한 글입니다.
1.2 DAO 분리
개발자가 객체를 설계할 때 가장 염두에 둬야 할 사항은 미래의 변화를 어떻게 대비할 것인가이다
여기서 변경 혹은 발전은 한 번에 한 가지 관심사항에 집중해서 일어난다
즉, 관심이 같은 것끼리는 모으고, 관심이 다른 것은 따로 떨어져 있게하는 관심사의 분리가 중요하다
📍 커넥션 만들기의 추출
UserDao - add() 메소드 관심사항
- DB와 연결을 위한 커넥션을 어떻게 가져올까
- 사용자 등록을 위해 DB에 보낼 SQL 문장을 담을 Statement를 어떻게 만들고 실행할까
- 작업이 끝나면 사용한 리소스인 Statement와 Connection 오브젝트 닫아주기
첫번째 "DB와 연결을 위한 커넥션을 어떻게 가져올까"
=> 커넥션을 가져오는 중복된 코드를 분리 => getConnection() 메소드
getConnection() 메소드를 추출해서 중복을 제거한 UserDao
package chapter1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class UserDao {
public void add(User user) throws SQLException, ClassNotFoundException {
Connection c = getConnection();
// 생략
}
public User get(String id) throws SQLException, ClassNotFoundException {
Connection c = getConnection();
// 생략
return user;
}
public Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("org.mariadb.jdbc.Driver");
Connection c = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/tobySpring"
, "root"
, "1234"
);
return c;
}
}
📍 변경사항에 대한 검증: 리팩토링과 테스트
main() 메소드를 이용하여 테스트하면 같은 결과가 출력된다(기존 데이터 삭제 후)
방금 한 작업은 UserDao의 기능에는 아무런 변화를 주지 않고 코드의 구조만 변경한다
기능이 추가되거나 바뀐 것은 없지만 코드가 이전보다 훨씬 깔끔해졌고 미래의 변화에 좀 더 손쉽게 대응할 수 있다
이런 작업을 리팩토링(Refactoring) 이라고 한다.
또한, 위에서 사용한 getConnection()이라고 하는 공통의 기능을 담당하는 메소드로
중복된 코드를 뽑아내는 것을 메소드 추출(extract method) 기법이라고 부른다.
📍 DB 커넥션 만들기의 독립
추후에 소스 코드를 공개하지 않고 고객에서 컴파일된 클래스 바이너리 파일만 제공했을 때
고객이 스스로 원하는 DB 커넥션 생성 방식을 적용하게 하고싶다면?
=> UserDao 코드 한 단계 더 분리
UserDao에서 메소드의 구현 코드를 제거하고 getConnection()을 추상 메소드로 만들어 놓는다.
=> 고객은 UserDao 클래스를 상속해서 각각 자신의 서브클래스를 만든다.
상속을 통한 확장 방법이 제공되는 UserDao
package chapter1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public abstract class UserDao2 {
public void add(User user) throws SQLException, ClassNotFoundException {
Connection c = getConnection();
// 생략
}
public User get(String id) throws SQLException, ClassNotFoundException {
Connection c = getConnection();
// 생략
return user;
}
// 구현 코드는 제거되고 추상 메소드로 바뀌었다
// 메소드의 구현은 서브클래스가 담당한다.
public abstract Connection getConnection() throws ClassNotFoundException, SQLException;
}
package chapter1;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class NUserDao extends UserDao2{
@Override
public Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("org.mariadb.jdbc.Driver");
Connection c = DriverManager.getConnection(
"jdbc:mariadb://localhost:3306/tobySpring"
, "root"
, "1234"
);
return c;
}
}
DAO의 핵심 기능인 어떻게 데이터를 등록하고 가져올 것인가 => UserDao
DB 연결 방법은 어떻게 할 것인가 => NUserDao
클래스 계층구조를 통해 두 개의 관심이 독립적으로 분리되면서 변경 작업 용이
=> UserDao의 코드는 수정 필요없이 DB 연결 기능을 새롭게 정의한 클래스 생성 가능
이렇게 슈퍼클래스에 기본적인 로직의 흐름을 만들고, 그 기능의 일부를 추상 메소드나
오버라이딩이 가능한 protected 메소드 등으로 만든 뒤 서브클래스에서 이런 메소드를 필요에 맞게
구현해서 사용하도록 하는 방법을 템플릿 메소드 패턴(Template Method Pattern)이라고 한다.
또한, 위 방식처럼 서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는 것을
팩토리 메소드 패턴(Factory Method Pattern)이라고 부르기도 한다.
(계속...)