1.2.1 관심사의 분리
객체 지향의 세계에서는 모든 것이 변한다.
- 미래의 변화에 어떻게 대비할 것인가?
- 변화의 폭을 최소한으로 줄여야한다.
- 분리와 확장을 고려한 설계
- 관심사의 분리(Separation of Concerns)
1.2.2 커넥션 만들기의 추출
UserDao의 관심사항
- DB와 연결을 위한 커넥션
- SQL 문장을 담은 Statement를 만들고 실행
- 작업이 끝난 후 Statement와 Connection 리소스 닫기
- 문제점
- 관심사가 너무 많다.
- add()메소드와 get()메소드에 DB커넥션을 가져오는 코드가 중복됨
중복 코드의 메소드 추출(메소드 추출기법)
- 중복되는 코드를 독립적인 메소드로 추출 (getConnection)
- DB 연결 방법이 바뀌면 getConnection() 메소드만 수정하면 된다.
public class UserDao {
public void add(User user) throws ClassNotFoundException, SQLException {
Connection c = getConnection();
// ...
}
public User get(String id) throws ClassNotFoundException, SQLException {
Connection c = getConnection();
// ...
}
private Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection c = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "spring", "book");
return c;
}
}
변경사항에 대한 검증
코드를 수정하게 되면 기능에 문제가 없음이 보장되지 않는다. 따라서 다시 한 번 검증이 필요하다.
main() 메서드를 만들어 테스트를 실행해보자.
테스트 실행 시 키 값 중복 오류가 발생하면 DB를 정리하고 다시 테스트한다.
public static void main(String[] args) throws ClassNotFoundException, SQLException{
UserDao dao = new UserDao();
User user = new User();
user.setId("whiteship");
user.setName("백기선");
user.setPassword("married");
dao.add(user);
System.out.println(user.getId() + "등록 성공");
User user2 = dao.get(user.getId());
System.out.println(user2.getName());
System.out.println(user2.getPassword());
System.out.println(user2.getId() + "조회 성공");
}
리팩토링
기존의 코드를 외부의 동작방식에는 변화없이 내부 구조를 변경해서 재구성하는 작업 또는 기술
내부 설계가 개선되어 코드를 이해하기가 더 편해지고, 변화에 효율적으로 대응 가능
1.2.3 DB 커넥션 만들기의 독립
상황
나의 DAO는 유명해졌다 !
N사와 D사에 납품을 하여야한다.
N사와 D사는 서로 다른 DB를 사용하고 있고, DB 커넥션을 가져오는 데 독자적인 방법을 쓰고 싶어한다.이후, DB 커넥션 방법이 종종 변경될 가능성이 있다.
소스 코드는 공개하고 싶지 않다.
상속을 통한 확장
- UserDao 코드를 한 번 더 분리
- getConnection()을 추상 메소드로 분리
- 메소드 코드는 없지만, 메소드 자체는 존재
- add(), get() 메소드에서 getConnection() 호출 코드 유지 가능
- N사와 D사는 NUserDao와 DUserDao라는 서브클래스를 만들어 getConnection()을 원하는대로 구현
public abstract class UserDao {
public void add(User user) throws ClassNotFoundException, SQLException {
Connection c = getConnection();
// ...
}
public User get(String id) throws ClassNotFoundException, SQLException {
Connection c = getConnection();
// ...
}
public abstract Connection getConnection() throws ClassNotFoundException, SQLException;
public static void main(String[] args) throws ClassNotFoundException, SQLException{
NUserDao dao = new NUserDao();
// ...
}
}
// Oracle을 사용하는 N사의 UserDao
class NUserDao extends UserDao {
@Override
public Connection getConnection() throws ClassNotFoundException, SQLException{
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection c = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:xe", "spring", "book");
return c;
}
}
// MySql을 사용하는 D사의 UserDao
class DUserDao extends UserDao {
@Override
public Connection getConnection() throws ClassNotFoundException, SQLException{
Class.forName("com.mysql.jdbc.Driver");
Connection c = DriverManager.getConnection("jdbc:mysql://localhost/springbook", "spring", "book");
return c;
}
}
관심사가 Class 레벨로 구분됨
- UserDao
- DAO 핵심기능; 어떻게 데이터를 등록하고 가져올 것인가?
- SQL문, 파라미터 바인딩, 쿼리 실행, 실행 결과 전달 등)
- 어떤 기능을 사용한다는 데에만 관심
- NUserDao / DUserDao
- DB 연결을 어떻게 할 것인가?
- 어떤 식으로 Connection 기능을 제공하는지
변경과 확장에 유연해졌다. DB가 변경되더라도, 다른 DB를 추가하더라도 UserDao 코드는 수정할 필요가 없음
참고 : 디자인 패턴
소프트웨어 설계 시 특정 상황에서 자주 만나는 문제를 해결하기 위해 사용할 수 있는 재사용 가능한 솔루션을 의미함.
- 대부분 객체 지향 설계에 관한 것이며, 객체 지향적 설계 원칙을 이용하여 문제를 해결
- 확장성 추구 방법
- 클래스 상속
- 오브젝트 합성
- 패턴의 결과로 나온 코드나 설계 구조는 대부분 비슷함
- 참고자료 : "GoF 디자인 패턴"(에릭 감마 외) / "Head First Design Patterns"(에릭 프리먼)
템플릿 메소드 패턴(Template Method Pattern)
상속을 통해 슈퍼클래스의 기능을 확장할 때 가장 대표적 방법
- 슈퍼클래스
- 변하지 않는 기능
- 기본적인 로직의 흐름(커넥션 가져오기, SQL 생성, 실행, 반환)
- 기능의 일부를 추상메서드나 오버라이딩 가능한 protected 메소드 등으로 추출
- 훅 메서드 : 선택적으로 구현(protected 메서드의 몸통을 비움)
- 추상 메서드 : 반드시 구현(abstract)
- 서브클래스
- 자주 변경되며 확장할 기능
- 슈퍼클래스에서 남겨둔 메서드를 필요에 맞게 구현
팩토리 메소드 패턴(Factory Method Pattern)
- 상속을 통해 기능을 확장하는 패턴으로 템플릿 메소드 패턴과 유사
- 슈퍼클래스 코드에서는 서브클래스에서 구현할 메소드를 호출해서 필요한 타입의 오브젝트를 리턴
- 서브클래스가 어떤 클래스의 오브젝트를 리턴할 지 알 수 없음
- 팩토리 메서드 : 오브젝트 생성 방법과 클래스를 결정할 수 있도록 미리 정의해 둔 메서드
- 슈퍼클래스의 기본 코드에서 독립
1.2 코드의 문제점
상속을 사용할 경우 여러가지 문제가 발생할 수 있다.
- 다중상속을 허용하지 않는 자바에서 다른 목적으로 UserDao의 상속을 적용하기 힘듬
- 상속 관계는 여저히 두 가지 다른 관심사에 대해 긴밀한 결합을 허용함
- 확장된 기능인 DB 커넥션을 생성하는 코드를 다른 DAO 클래스에 적용할 수 없음
- UserDao 외 다른 DAO가 계속 만들어지면 그 때마다 getConnection() 구현 코드가 중복됨
' Spring > 토비의 스프링 3.1' 카테고리의 다른 글
1.6 싱글톤 레지스트리와 오브젝트 스코프 (0) | 2019.01.09 |
---|---|
1.5 스프링의 IoC (0) | 2019.01.07 |
1.4 제어의 역전(IoC) (0) | 2019.01.07 |
1.3 DAO의 확장 (0) | 2019.01.07 |
1.1 초난감 DAO (0) | 2019.01.03 |