3-2. 인터페이스의 도입
인터페이스는 자신을 구현한 클래스에 대한 구체적인 정보는 모두 감춰버린다.
인터페이스로 추상화해놓은 최소한의 통로를 통해 접근하는 쪽에서는 오브젝트를 만들 때 사용할 클래스가 무엇인지 몰라도 된다.
인터페이스는 어떤 일을 하겠다는 기능만 정의해놓은 것이다. 따라서 인터페이스에서는 어떻게 하겠다는 구현 방법은 나타나 있지 않다.
public interface ConnectionMaker {
public Connection makeConnection() throws ClassNotFoundException, SQLException;
}
makeConneciont() 메소드를 호출하면 Connection 타입의 오브젝트를 만들어서 돌려줄 것을 기대할 수 있다.
public class ChordpliConnectionMaker implements ConnectionMaker {
@Override
public Connection makeConnection() throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection c = DriverManager.getConnection("jdbc:mysql://localhost:3306/tobi", "root", "1234");
return c;
}
}
Connection 인터페이스를 구현한 클래스를 만들고, 자신들의 DB 연결 기술을 이용하여 DB 커넥션을 가져오도록 메소드를 작성한다.
나는 ChordConnectionMaker 클래스를 작성하여 DB 정보를 입력하였다.
public class UserDao {
private ConnectionMaker connectionMaker; // 인터페이스를 통해 오브젝트에 접근하므로 구체적인 클래스 정보를 알 필요가 없다.
public UserDao(){
connectionMaker = new ChordpliConnectionMaker(); // -> 하지만 여기서 클래스 이름이 나와버린다. !!!
}
public void add(User user) throws ClassNotFoundException, SQLException {
Connection c = connectionMaker.makeConnection(); // 인터페이스에 정의된 메소드 사용,
//클래스가 바뀐다고 메소드 이름이 변경될 걱정은 없다.
...
}
public User get(String id) throws ClassNotFoundException, SQLException {
Connection c = connectionMaker.makeConnection();
...
}
}
인터페이스를 이용하게 만들어서 DB 커넥션을 제공하는 클래스에 대한 구체적인 정보는 모두 제거가 가능했지만, 초기에 한 번 어떤 클래스의 오브젝트를 사용할지 결정하는 생성자의 코드는 제거되지 않고 남아있다.
결국, 다시 원점.
여전히 소스코드를 제공하여 고객에게 직접 수정하라고 하지 않고는 고객에게 자유로운 DB 커넥션 확장 기능을 가진 UserDao를 제공할 수가 없다.
3-3. 관계 설정 책임의 분리
여전히 UserDao에는 어떤 ConnectionMaker 구현 클래스를 사용할지 결정하는 코드가 남아있다.
인터페이스를 이용한 분리에도 불구하고 여전히 UserDao 변경 없이는 DB 커넥션 기능의 확장이 자유롭지 못한데, 그 이유는 UserDao 안에 분리되지 않은, 또 다른 관심사항이 존재하고 있기 때문이다.
new ChordpliMaker()
라는 코드는 UserDao가 어떤 ConnectionMaker 구현 클래스의 오브젝트를 이용하게 할지를 결정한다.
간단히, UserDao와 UserDao가 사용할 ConnectionMaker의 특정 구현 클래스 사이의 관계를 설정해주는 것에 대한 관심이다.
UserDao의 모든 코드는 ConnectionMaker 인터페이스 외에는 어떤 클래스와도 관계를 가져서는 안 되게 해야한다.
물론 UserDao 오브젝트가 동작하려면 특정 클래스의 오브젝트와 관계를 맺어야 한다.
하지만 클래스 사이에 관계가 만들어진 것은 아니고, 단지 오브젝트 사이에 다이나믹한 관계가 만들어지는 것이다.
클래스 사이의 관계는 코드에 다른 클래스의 이름이 나타나기 때문에 만들어지는 것이다. 하지만 오브젝트사이의 관계는 그렇지 않다. 특정 클래스를 전혀 알지 못하더라도 해당 클래스가 구현한 인터페이스를 사용했다면, 그 클래스의 오브젝트를 인터페이스 타입으로 받아서 사용할 수 있다.
public UserDao(ConnectionMaker connectionMaker){
this.connectionMaker = connectionMaker;
}
자 이제 다른 관심을 분리해서 클라이언트에게 떠넘기자!
class UserDaoTest {
public static void main(String[] args) throws SQLException, ClassNotFoundException {
ConnectionMaker connectionMaker = new ChordpliConnectionMaker();
UserDao userDao = new UserDao(connectionMaker);
}
}
UserDaoTest는 UserDao와 ConnectionMaker 구현 클래스와의 런타임 오브젝트 의존 관계를 설정하는 책임을 당당해야 한다. 그래서 특정 ConnectionMaker 구현 클래스의 오브젝트를 만들고, UserDao 생성자 파라미터에 넣어 두 개의 오브젝트를 연결해준다.
이렇게 인터페이스를 도입하고 클라이언트의 도움을 얻는 방법은 상속을 사용해 비슷한 시도를 했을 경우에 비해서 훨씬 유연하다.
DAO가 아무리 많아져도 DB 접속 방법에 대한 관심은 오로지 한 군데에 집중되기 할 수 있고, DB 접속 방법을 변경해야 할 때도 오직 한 곳의 코드만 수정하면 된다.
3-4. 원칙과 패턴
개방 폐쇄 원칙(OCP, Open-Closed Principle)
클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다.
UserDao는 DB 연결 방법이라는 기능을 확장하는 데 열려있다. 동시에 UserDao 자신의 핵심 기능을 구현한 코드는 그런 변화에 영향을 받지 않고 유지할 수 있으므로 변경에는 닫혀있다고 말할 수 있다.
인터페이스를 통해 제공되는 확장 포인트는 확장을 위해 활짝 개방되어 있다. 반면 인터페이스를 이용하는 클래스는 자신의 변화가 불필요하게 일어나지 않도록 굳게 폐쇄되어 있다.
높은 응집도와 낮은 응집도
응집도가 높다는 건 하나의 모듈, 클래스가 하나의 책임 또는 관심사에만 집중되어 있다는 뜻.
높은 응집도
변경이 일어날 때 모듈의 많은 부분이 함께 바뀐다면 응집도가 높다고 말할 수 있다.
낮은 결합도
책임과 관심사가 다른 오브젝트 또는 모듈과는 낮은 결합도, 즉 느슨하게 연결된 형태를 유지하는 것이 바람직하다.
여기서 결합도란 '하나의 오브젝트가 변경이 일어날 때에 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도'
낮은 결합도란 결국, 하나의 변경이 발생할 때 마치 파문이 이는 것처럼 여타 모듈과 객체로 변경에 대한 요구가 전파되지 않는 상태를 말한다.
전략 패턴
자신의 기능 맥락(context)에서, 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴.
객체지향 설계 원칙(SOLID)
- SRP(The Single Responsibility Principle) : 단일 책임 원칙
- OCP(The Open Closed Principle) : 개방 폐쇄 원칙
- LSP(The Liskov Substitution Principle) : 리스코프 치환 원칙
- ISP(The Interface Segregation Principle) : 인터페이스 분리 원치
- DIP(The Dependency Inversion Principle) : 의존관계 역전 원칙
'Server > Spring&Spring Boot' 카테고리의 다른 글
[토비의 스프링] 제어의 역전 - 오브젝트 팩토리 활용, 제어권의 이전을 통한 제어관계 역전 (0) | 2022.10.29 |
---|---|
[토비의 스프링] 제어의 역전 - 오브젝트 팩토리 (0) | 2022.10.28 |
[토비의 스프링] 오브젝트와 의존관계 - DAO의 확장 (0) | 2022.10.24 |
[토비의 스프링] 오브젝트와 의존관계 - DAO의 분리 (0) | 2022.10.24 |
[토비의 스프링] 오브젝트와 의존관계 - 초난감 DAO (0) | 2022.10.24 |