Back-end/Spring

[Spring] 객체지향적 DAO - 확장 (전략 패턴)

객체지향 세계에서 모든 오브젝트는 변한다고 했는데, 모든 오브젝트가 다 동일한 방식으로 변하진 않습니다.

 

이전 글에서, 상속을 통해 DAO를 분리해봤습니다. 

 

하지만 상속에는 여러 가지 문제점이 있습니다.

 

참고: https://withseungryu.tistory.com/67

 

[Spring] 객체지향적 DAO 만들기 - 분리

👉 객체지향의 세계에서는 모든것이 변하는데, 쉽게 말하면 객체에 대한 설계와 이를 구현한 코드가 변한다는 것입니다. 그렇기 때문에 개발자는 객체를 설계할 때 바로 미래의 변화를 어떻게

withseungryu.tistory.com

 

이러한 문제들이 있기 때문에, 이번에는 상속 관계과 아닌 독립적인 클래스로 만들어보겠습니다.

 

즉, DB Connection 부분을 서브클래스가 아닌, 별도의 클래스에 넣어보겠습니다.

public class UserDao_extension {
    
    private SimpleConnectionMaker simpleConnectionMaker;
    
    public UserDao() {
        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();
        //...생략
    }
    
    
}

 

하지만 여기서도 문제점이 발생하는데,

만약 두 명의 고객이 UserDao 클래스만 공급하고 상속을 통해 DB Connection 기능을 확장해서 사용하는 것이 불가능합니다.

 

인터페이스 도입

 

위 문제를 해결하기 위해 상속 대신 인터페이스를 도입해 확장을 해야합니다.

 

💡 인터페이스란?

 '자바가 추상화를 위해 제공하는 가장 유용한 도구' , '어떤 일을 하겠다는 기능만 정의해놓은 코드'

 

인터페이스를 통해 UserDao와 ConnectionMaker간의 서로 긴밀하게 연결되어 있지 않도록 중간에 추상적인 느슨한 연결고리를 만들어줄 수 있습니다.

 

ConnectionMaker 인터페이스

public interface ConnectionMaker {
    public Connection makeConnection() throws ClassNotFoundException, SQLException;
}

 아래와 같이 implements를 사용해 인터페이스를 구현할 수 있습니다.

 

ConnectionMaker 구현 클래스

public class NConnectionMaker implements ConnectionMaker {
    //..생략
    public Connection makeConnection() throws ClassNotFoundException, SQLException {
        // Naver의 독자적인 방법으로 Connection을 생성하는 코드
    }
}

 

ConnectionMaker 인터페이스를 사용해 아래와 같이 UserDao를 개선할 수 있습니다.

public class UserDao{
    private ConnectionMaker connectionMaker;
    
    public UserDao() {
        connectionMaker = new NConnectionMaker();
    }
    
    public void add(User user) throws ClassNotFoundException, SQLException {
        Connection c = connectionMaker.makeConnection();
        //...생략
    }
    
    public User get(String id) throws ClassNotFoundException, SQLException {
        Connection c = connectionMaker.makeConnection();
        //...생략
    }
}

 

 하지만 위 코드에도 문제가 있습니다.

 만약 Naver가 아닌 다른 회사에서 Dao를 사용하고 싶을 때, 생성자에 있는 아래 코드의 NConnectionMaker(); 부분을   바   꿔줘야 하기 때문입니다.

 

 앞서 말했듯이, 대부분의 개발자는 소스코드를 고객에게 노출시키길 원하지 않습니다.

 그렇기 때문에 이 부분을 바꿔줘야 하는데, 관계설정 책임의 분리를 하여 해결할 수 있습니다.

connectionMaker = new NConnectionMaker();

 

관계 설정 책임의 분리

 

기존 UserDao의 관심사가 아닌 부분을 서버 부분이 아닌 클라이언트 부분으로 분리를 해주는 것입니다.

즉, 클라이언트 부분에서 UserDao와 관심사가 아닌 부분 간의 오브젝트(객체) 관계를 설정해주는 것입니다.

 

이것이 가능한 이유는 코드에서는 특정 클래스를 전혀 알지 못하더라도 해당 클래스가 구현한 인터페이스를 사용했다면, 그 클래스의 오브젝트를 인터페이스 타입으로 받아서 사용할 수 있기 때문입니다.

 

따라서 두 오브젝트 사이에 런타임 사용관계 또는 링크, 또는 의존관계라고 불리는 관계를 맺어주면 됩니다.

 

 

구현을 해보면, UserDao의 생성자에 분리된 connectionMaker를 파라미터에 추가해주고,

public UserDao(ConnectionMaker connectionMaker) {
        this.connectionMaker = connectionMaker
    }

 

클라이언트 부분인 UserDaoTest에서 생성한 파라미터에 고객에 알맞은 connectionMaker를 넣어주면 됩니다.

public class UserDaoTest {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        ConnectionMaker connectionMaker = new NConnectionMaker(); //Naver의 맞는 ConnectionMaker
        
        UserDao dao = new UserDao(connectionMaker);
    }
}

 

🧾 정리 

 

이렇게 만들어진 UserDaoTest - UserDao - ConnectionMaker 구조를

디자인 패턴의 시각으로 전략 패턴(Strategy Pattern)이라 할 수 있습니다.

 

💡 전략 패턴이란?

 

 디자인 패턴의 꼿이라고 불릴 만큼 다양하게 사용되며, 개방 폐쇄 원칙의 실현에도 가장 잘 들어 맞는 패턴입니다.

 전략 패턴은 자신의 기능 맥락에서, 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 외부로 분리시키고,

 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴입니다.

 

참고 문서 : 

토비의 스프링 3 (이일민)

반응형