이전 장에서 "객체지향 세계에서는 비즈니스 요구사항에 따라서 오브젝트에 대한 설계가 변할 수 있다." 라고 했다.
그런데 여러 오브젝트들은 서로 다른 변화의 성격을 갖고 있다.
변화의 성격이 다르다는 건 변화의 이유와 시기, 주기 등이 다르다는 뜻이다.
관심사를 분리하여 코드를 작성하는 이유
변화의 성격이 다른 것을 분리해서, 서로 영향을 주지 않은 채로 각각 필요한 시점에 독립적으로 변경할 수 있게 하기 위해서다.
이전 장에서 상속을 통한 관심사 분리의 문제점에 대하여 다루었다.
이번에는 관심사가 다르고 변화의 성격이 다른 코드를 완전히 독립적인 클래스로 만들어서 분리할 것이다.
방법
public class UserDao {
private SimpleConnectionMaker simpleConnectionMaker;
// 상태를 관리하는 것도 아니닌 한번만 만들어 인스턴스 변수에 저장해두고 메소드에서 사용하게 한다.
public UserDao() {
this.simpleConnectionMaker = new SimpleConnectionMaker();
}
public void add(User user) throws ClassNotFoundException, SQLException {
Connection c = this.simpleConnectionMaker.makeNewConnection();
...
}
public void get(String id) throws ClassNotFoundException, SQLException {
Connection c = this.simpleConnectionMaker.makeNewConnection();
...
}
}
DB 연결에 대한 관심사를 독립시킨 SimpleConnectionMaker는 아래와 같이 만든다.
public class SimpleConnectionMaker {
public Connection getConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
// Connection 을 가져온다.
Connection c = DriverManager.makeNewConnection(
"jdbc:mysql://localhost:3306/toby?useSSL=false", "root", "password"
);
return c;
}
}
위와 같이 코드를 리팩토링 한 후에 테스트를 진행하여 기능에 변화가 없다는 것 을 검증하여야 한다.
main()메소드를 다시 실행해서 수정하기 전과 동일한 결과가 나오는지 확인해보자.
변화의 성격이 다른 코드를 독립된 클래스를 생성하여 분리한 것은 잘한 것 같다.
하지만, N 사와 D 사에 UserDao 클래스만 공급하고 상속을 통해 DB 연결 시능을 확장해서 사용하게 했던 게 다시 불가능해졌다.
즉 클래스를 분리한 경우에도 상속을 이용했을 때와 마찬가지로 자유로운 확장이 가능하게 하려면 2가지 문제를 해결해야 한다.
각 클라이언트가 DB 연결을 가져오는 메소드의 이름을 다르게 했을때 발생하는 문제
DB연결을 제공하는 클래스가 어떤 것인지를 UserDao가 구체적으로 알고 있어야 한다.
위 문제의 가장 좋은 해결책은 두개의 클래스(UserDao & SimpleConnectionMaker)가 서로 긴밀하게 연결되어 있지 않도록 중간에 추상적인 느슨한 연결고리를 만들어 주는 것이다.
인터페이스
- 자바가 추상화를 위해 제공하는 가장 유용한 도구
- 자신을 구현한 클래스에 대한 구체적인 정보는 모두 감춰버린다.
추상화 : 어떤 것들의 공통적인 성격을 뽑아내 이를 따로 분리해내는 작업
아래 그림은 인터페이스를 도입한 후 클래스의 관계를 표현한 것이다.
ConnectionMaker 인터페이스
public interface ConnectionMaker {
public Connection makeNewConnection() throws ClassNotFoundException, SQLException;
}
ConnectionMaker 구현 클래스
public class DConnectionMaker implements ConnectionMaker {
public Connection makeNewConnection() throws ClassNotFoundException, SQLException {
// D 사의 독자적인 방법으로 Connection을 생성하는 코드
}
}
ConnectionMaker 인터페이스를 사용하도록 개선한 UserDao
public class UserDao {
// 인터페이스를 통해 오브젝트에 접근하므로 구체적인 클래스 정보를 알 필요가 없다.
private ConnectionMaker connectionMaker;
public UserDao() {
// 하지만! 여기에는 특정 클래스 이름이 나온다.
this.connectionMake = new DConnectionMaker();
}
public void add(User user) throws ClassNotFoundException, SQLException {
Connection c = this.connectionMaker.makeNewConnection();
...
}
public void get(String id) throws ClassNotFoundException, SQLException {
Connection c = this.connectionMaker.makeNewConnection();
...
}
}
UserDao의 add(), get() 메소드와 필드에는 ConnectionMaker라는 인터페이스와 인터페이스의 메소드인 makeNewConnection() 만 사용하도록 했다.
그러므로 N 사와 D 사가 DB 연결 클래스를 다시 만든다고 해도, UserDao의 코드를 고칠 필요가 없을 것 같다.
하지만, UserDao의 코드를 자세히 보면 DConnectionMaker 라는 클래스 이름이 보인다.
여전히 UserDao에 특정 클라이언트에 대한 정보를 설정해 줘야하는 코드가 남아있다.
여전히 UserDao에는 어떤 ConnectionMaker 구현 클래스를 사용할지를 결정하는 코드가 남아있다.
그 이유는 UserDao 안에 분리되지 않은, 또 다른 관심사항이 존재하고 있기 때문이다.
이 관심사를 UserDao에서 분리하지 않으면 UserDao는 독립적으로 확장 가능한 클래스가 될 수 없다.
해결법
즉 클래스 사이에 관계를 만들어지는 것이 아닌. 오브젝트 사이에 다이내믹한 관계가 만들어지는 것이다.
클라스 사이의 관계 : 코드에 다른 클래스 이름이 나타나기 때문에 만들어지는 것
오브젝트 사이의 관계 : 코드에서 특정 클래스의 이름을 몰라도, 해당 클래스의 오브젝트를 인터페이스 타입으로 받아서 사용할 수 있다.
객체지향 프로그램의 다형성이라는 특징 덕분이다.
오브젝트 사이의 관계는 런타임 시점의 오브젝트 사이에 생기는 관계이다.
의존관계 또는 런타임 사용관계, 링크라고 불린다.
UserDao의 클라이언트 : main()
UserDao의 생성자를 수정해서 클라이언트가 미리 만들어둔 특정 클래스인 ConnectionMaker의 오브젝트를 전달 받을 수 있도록 파라미터를 하나 추가한다.
수정한 생성자
public class UserDao {
// 인터페이스를 통해 오브젝트에 접근하므로 구체적인 클래스 정보를 알 필요가 없다.
private ConnectionMaker connectionMaker;
public UserDao(ConnectionMaker connectionMaker) {
this.connectionMake = connectionMaker
}
}
관계설정 책임이 추가된 UserDao 클라이언트인 main( ) 메소드
public class main {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
// UserDao가 사용할 ConnectionMaker 구현 클래스를 결정하고 오브젝트를 만든다.
ConnectionMaker connectionMaker = new DConnectionMaker();
// 1. UserDao 생성
// 2. 사용할 ConnectionMaker 타입의 오브젝트 제공.
// 결국 두 오브젝트 사이의 의존관계 설정
UserDao userDao = new UserDao(connectionMaker);
}
}
UserDao의 클라이언트인 main은 UserDao의 변경없이 N 사와 D 사가 자신들을 위한 DB 연결 클래스를 만들어서 사용할 수 있게 되었다.
지금까지 초난감 DAO 코드를 개선해온 결과를 객체지향 기술의 여러 가지 이론을 통해 설명 하려고 한다.
높은 응집도
낮은 결합도
소스코드 : github