일반적인 JDBC DAO 메소드
외부로 부터 파라미터를 받아서 DB에 INSERT 하는 메소드이다.
하지만 주입하는 파라미터가 변경되거나 또는 DB에 주입하는 테이블이 변경되면
우리는 또다시 그에 맞게 메서드를 만들어줘야 할까?
결국 무언가 변경될때 코드도 지속적으로 변경해줘야하는 점에서 매우 비효율적이라고 느꼈고
리펙토링의 필요성을 매우 느꼈다.
미숙할지 언정 리펙토링을 해보며 이글을 기록한다.
1.변경될수 있는 부분을 분리하자
개방 폐쇠 원칙(OOP)를 잘 지키는 구조이다.
오브젝트를 둘로 분히라고 클래스 레벨에서는 인터페이스를 통해서만 의존하도록 만드는 전략 패턴이다.
인터페이스 생성
public interface StatementStrategy {
PreparedStatement makePreparedStatement(Connection c)throws SQLException;
}
구현클래스 생성
public class AddStatement implements StatementStrategy{
private TB_IF_IFISMilestoneArrVO vo;
public AddStatement(TB_IF_IFISMilestoneArrVO vo) {
this.vo = vo;
}
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
PreparedStatement psmt =
c.prepareStatement("insert into TB_IF_IFIS_MilestoneArr ("
+ " message_type, \r\n"
+ " date_Of_Flight, \r\n"
+ " airport, \r\n"
+ " flight_Carrier) "
+ "value(?,?,?,?) ");
psmt.setString(1, vo.getMessage_Type());
psmt.setString(2, vo.getDate_Of_Flight());
psmt.setString(3, vo.getAirport());
psmt.setString(4, vo.getFlight_Carrier());
return psmt;
}
}
컨텍스트 코드
/**
* 메소드로 분리한 try~catch 컨텍스트 코드
* */
public void jdbcContextWithStatementStrategy(StatementStrategy stmt)throws SQLException{
try( Connection conn = DbcpConnection.getConnection();
PreparedStatement psmt = stmt.makePreparedStatement(conn);) {
psmt.executeUpdate();
} catch (Exception e) {
// TODO: handle exception
}
}
/**
* 클라이언트의 책임을 담당할 메소드
* */
public void addVO(TB_IF_IFISMilestoneDepVO vo)throws SQLException {
//선정한 전략 클래스의 오브젝트 생성
StatementStrategy st = new AddStatement(vo);
//컨텍스트 호출, 전략 오브젝트 전달
jdbcContextWithStatementStrategy(st);
}
위의 구조로 대략적인 전략패턴의 모습을 갗추며 리펙토링 할 수 있었다.
다만 아쉬운 점은 컨텍스트 코드에 클라이언트 사용코드가 분리가 안됬다는 점이다.
또한 DAO마다(ex, insert delete update select) 구현 클래스를 만들어 줘야 하며, 각각이
전달 파라미터가 다를 경우 내부 인스턴스 변수로 일일히 생성해줘야 하는 등 클래스 파일의 개수가 늘어난다는 불편함이 아직 존재한다.
2.내부클래스,익명클래스를 사용하여 DAO 마다 늘어나는 클래스의 개수를 줄여보자
내부클래스 사용
/**
* 메소드로 분리한 try~catch 컨텍스트 코드
* */
public void jdbcContextWithStatementStrategy(StatementStrategy stmt)throws SQLException{
try( Connection conn = DbcpConnection.getConnection();
PreparedStatement psmt = stmt.makePreparedStatement(conn);) {
psmt.executeUpdate();
} catch (Exception e) {
// TODO: handle exception
}
}
//로컬 클래스
public void addVO(final TB_IF_IFISMilestoneDepVO vo)throws SQLException{
class AddStatement implements StatementStrategy{
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
PreparedStatement psmt =
c.prepareStatement("insert into TB_IF_IFIS_MilestoneArr ("
+ " message_type, \r\n"
+ " date_Of_Flight, \r\n"
+ " airport, \r\n"
+ " flight_Carrier) "
+ "value(?,?,?,?) ");
psmt.setString(1, vo.getMessage_Type());
psmt.setString(2, vo.getDate_Of_Flight());
psmt.setString(3, vo.getAirport());
psmt.setString(4, vo.getFlight_Carrier());
return psmt;
}
}
}
//익명 내부 클래스
public void addVO(final TB_IF_IFISMilestoneDepVO vo)throws SQLException{
//컨텍스트 호출
jdbcContextWithStatementStrategy(new StatementStrategy() {
@Override
public PreparedStatement makePreparedStatement(Connection c) throws SQLException {
PreparedStatement psmt =
c.prepareStatement("insert into TB_IF_IFIS_MilestoneArr ("
+ " message_type, \r\n"
+ " date_Of_Flight, \r\n"
+ " airport, \r\n"
+ " flight_Carrier) "
+ "value(?,?,?,?) ");
psmt.setString(1, vo.getMessage_Type());
psmt.setString(2, vo.getDate_Of_Flight());
psmt.setString(3, vo.getAirport());
psmt.setString(4, vo.getFlight_Carrier());
return psmt;
}
});
}
내부클래스 내에서 외부 변수를 사용하려면 외부 변수는 무조건 final 이 선언되어있어야 함을 잊지말자
위와 같이 익명/내부 클래스 사용시, DAO 마다 추가해야 했던 클래스를 줄일 수 있으며 로컬 변수들을 바로바로 가져다 쓸수있는 장점이 있다.
결국 공통 코드는 분리해둠으로써 호출로서 재사용이 가능하다.
이제는 변경할 부분이 있으면 전체를 수정하지 않고, 변경될 부분만 부분적으로 수정해주면 됨으로 좀더 간편해졌다.
'Language > 자바' 카테고리의 다른 글
Java 8 date/time type `java.time.LocalDateTime` not supported by default 문제 해결 (0) | 2022.11.10 |
---|---|
java.util.ConcurrentModificationException 예외 (0) | 2022.08.16 |
log4j 파일기록 JDK 버전문제 (0) | 2022.05.31 |
[자바]23. ResultSet 을 Java VO 타입 을 JSON 형식으로 변환하기 (0) | 2022.01.25 |
[자바]23. Properties 사용하여 프로퍼티 파일 읽기 (0) | 2022.01.20 |