Today I Learned_230227
토비의 스프링 3.1
DAO의 확장
- 관심사에 따라 분리한 오브젝트들은 각기 독특한 변화의 특징이 있음
- 변화의 성격이 다르다는 것은 변화의 이유, 시기, 주기가 다르다는 것
- 변화의 성격이 다른 것을 분리해서, 서로 영향을 주지 않은 채로 각각 필요한 시점에 독립적으로 변경할 수 있도록 해야 함
클래스의 분리
- 관심사가 다른 것들을 독립적인 클래스로 분리
- 독립 클래스로 분리하고 한 클래스가 다른 클래스를 사용하도록 분리
- 예제 : 사용자 정보를 저장하고 가져오는 방법 / DB연결하고 커넥션을 가져오는 방법을 분리
DB연결 분리
public class SimpleConnectionMaker {
public Connection makeNewConnection() throws ClassNotFoundException, SQLException {
Class.forName("jdbc 드라이버 이름");
Connection c = DriverManager.getConnection("jdbc url", "user", "pwd");
return c;
}
}
사용자 정보 저장하고 가져오기
public class UserDao {
private SimpleConnectionMaker simpleConnectionMaker;
public UserDao(SimpleConnectionMaker simpleConnectionMaker) {
this.simpleConnectionMaker = new SimpleConnectionMaker();
}
public void add(User user) throws ClassNotFoundException, SQLException { //예외는 메소드 밖으로 던지기
Connection c = simpleConnectionMaker.makeNewConnection();
....
}
public User get(String id) throws ClassNotFoundException, SQLException {
Connection c = simpleConnectionMaker.makeNewConnection();
....
}
}
- SimpleConnectionMaker로 분리
- UserDao가 특정 클래스(SimpleConnectionMaker)에 종속되어 있어 상속을 사용했을 때 코드 수정 없이 DB 커넥션 생성 기능을 변경할 수 없음
- 문제점 : UserDao가 DB 커넥션에 대한 정보를 너무 많이 갖고 있다
- 구현체(SimpleConnectionMaker)에 너무 의존적이다
- 어떤 클래스가 쓰일지, 어떤 메소드를 가지고 있는지 구체적인 방법에 종속됨
인터페이스의 도입
- 추상적인 느슨한 연결고리
- 두 개의 클래스가 서로 긴밀하게 연결되지 않도록
- 추상화 : 어떤 것들의 공통적인 성격을 뽑아내어 이를 따로 분리하는 작업
- 자바에서는 인터페이스를 통해 추상화를 진행함
- 인터페이스에서는 어떤 일을 할지만 정해놓고, 어떻게 하는지는 구체 클래스들이 결정
인터페이스 생성
public interface ConnectionMaker {
public Connection makeConnection() throws ClassNotFoundException, SQLException;
}
인터페이스 구현
public class DConnectionMaker implements ConnectionMaker{
@Override
public Connection makeConnection() throws ClassNotFoundException, SQLException {
//Connection 생성
}
}
인터페이스 사용하여 DB 커넥션 가져오기
public class UserDao {
private ConnectionMaker connectionMaker;
public UserDao() {
this.connectionMaker = new DConnectionMaker();
}
public void add(User user) throws ClassNotFoundException, SQLException { //예외는 메소드 밖으로 던지기
Connection c = connectionMaker.makeConnection();
}
public User get(String id) throws ClassNotFoundException, SQLException {
Connection c = connectionMaker.makeConnection();
}
}
- 그럼에도 불구하고 UserDao의 생성자에서 DConnectionMaker를 직접 사용하고 있음
- 커넥션을 변경하려면 UserDao를 변경해야 함
- 분리되지 않은 또다른 관심사항(DConnectionMaker)이 존재
관계설정 책임의 분리
- UserDao와 UserDao가 사용할 ConnectionMaker의 구현 클래스 사이의 관계를 설정해주는 역할이 필요
- 사용되는 쪽이 사용하는 쪽에게 서비스를 제공
- 서비스 : 사용되는 오브젝트
- 클라이언트 : 사용하는 오브젝트
- 클라이언트 오브젝트가 구현 클래스간의 관계를 설정하도록 하면 된다
- 클래스 사이에 관계가 만들어진다 : 한 클래스가 인터페이스 없이 다른 클래스를 직접 사용한다
- 코드에 클래스 이름이 나타나는 상황
- 오브젝트 사이에 관계가 만들어진다 : 런타임 시에 한쪽이 다른 오브젝트의 레퍼런스를 갖고 있다
- 런타임 시에 서로 사용하게 다이나믹한 관계를 맺어주면 됨
- 오브젝트 사이 관계 설정
- 직접 생성자를 호출하여 설정
- 외부에서 만들어 준 것 가져오기 (메소드 파라미터, 생성자 파라미터 등으로 전달)
- 다형성 활용 : 클래스의 오브젝트를 인터페이스 타입으로 받아서 사용
- 클라이언트의 책임
- 클래스들을 이용해 런타임 오브젝트 관계를 갖는 구조로 만들어주는 것
- 모델링 시에는 없었지만 다이나믹하게 생성됨
- 클라이언트는 구현 클래스(세부 전략)를 선택하고 선택한 오브젝트를 생성해 사용해야 할 곳에 던져줌
- 기존에는 사용해야 할 곳의 생성자에 책임이 있었다
- 확장성이 떨어지는 상황
- 대개는 main 메소드가 클라이언트에 해당
책임을 클라이언트에 떠넘기기
public UserDao(ConnectionMaker connectionMaker) {
this.connectionMaker = connectionMaker;
}
public class UserDaoTest {
public static void main(String[] args) throws ClassNotFoundException, SQLException {
DConnectionMaker connectionMaker = new DConnectionMaker();
UserDao dao = new UserDao(connectionMaker);
}
}
- 클라이언트인 UserDaoTest가 구현 클래스간의 런타임 오브젝트 의존관계를 설정하는 책임 담당
- UserDao는 자신의 관심사만 보면 됨
- 최초 DAO
- UserDao에서 DB 커넥션 생성, 자원 반환, 사용자 관련 조작 모두 수행
- DB 관련 코드가 변경되면 UserDao의 모든 메소드를 찾아야 함
- 관심사의 분리
- DB 커넥션 가져오는 중복 코드 분리
- DB 관련 코드가 변경되면 DB 관련 메소드만 수정하면 됨
- 한 가지 방법으로면 DB 커넥션 가능
- 상속을 통한 확장
- 여러 방법으로 DB 커넥션을 가져오고픔
- UserDao에 DB 커넥션 관련 추상 메소드를 생성하고 서브클래스에서 상속을 받아 사용
- 자바는 클래스의 다중상속을 허용하지 않기에 한계가 있음
- 상속으로 이루어진 관계는 밀접함
- 클래스의 분리
- 상속을 통한 관계가 아닌 서로 사용하는 관계로 분리
- 한 클래스가 다른 클래스에 대해 너무 많이 알고 있어서 확장이 어려워짐
- 인터페이스의 도입
- 기능에만 관심을 가지고 어떻게 구현되었는지는 관심 가지지 않음
- 어찌되었든 구체 클래스를 찾아야 함
- 관계설정 책임의 분리
- 오브젝트 사이의 관계를 클라이언트가 설정하도록 책임을 떠넘김