토비의 스프링 - 1장. 오브젝트와 의존관계(2)

 Date: 2022-02-05

1.3 DAO의 확장
1.3.1 클래스의 분리
1.3.2 인터페이스의 도입
1.3.3 관계 설정 책임의 분리
1.3.4 원칙과 패턴

1.3 DAO의 확장

  • 지금까지 데이터 액세스 로직을 어떻게 만들 것인가DB 연결을 어떤 방법으로 할 것인가라는 두 개의 관심을 상하위 클래스로 분리시켰다.
  • 변화의 성격이 다르다는 건 변화의 이유와 시기, 주기 등이 다르다는 뜻이다.

클래스의 분리

  • SimpleConnectionMaker라는 새로운 클래스를 만들고 DB 생성 기능을 그 안에 넣는다.
  • 예제 코드
  • 클래스를 분리한 경우에도 상속을 이용했을 때와 마찬가지로 자유로운 확장이 가능하게 하려면 두 가지 문제를 해결해야 한다.
    • SimpleConnectionMaker의 메소드가 달라지면 일일이 수정해야 한다는 점
    • DB 커넥션을 제공하는 클래스가 어떤 것인지를 UserDao가 구체적으로 알고 있어야 한다는 점

    이런 문제의 근본적인 원인은 UserDao가 바뀔 수 있는 정보, 즉 DB 커넥션을 가져오는 클래스에 대해 너무 많이 알고 있기 때문이다.

인터페이스의 도입

  • 가장 좋은 해결책은 두 개의 클래스가 서로 긴밀하게 연결되어 있지 않도록 중간에 추상적인 느슨한 연결고리를 만들어주는 것이다.
  • 추상화란 어떤 것들의 공통적인 성격을 뽑아내어 이를 따로 분리해내는 작업이다.
  • 자바가 추상화를 위해 제공하는 가장 유용한 도구는 바로 인터페이스다.

관계 설정 책임의 분리

  • 인터페이스를 이용한 분리에도 불구하고 여전히 UserDao 변경 없이 DB 커넥션 기능의 확장이 자유롭지 못하다.

      public UserDao() {
          connectionMaker = new DConnectionMaker();
      }
    
    • 어떤 ConnectionMaker 구현 클래스의 오브젝트를 이용하게 할지에 대한 관심사가 UserDao 생성자에 포함되어 있다.
    • 이 관심사를 UserDao에서 분리하지 않으면 UserDao는 결코 독립적으로 확장 가능한 클래스가 될 수 없다.
    • ConnectionMaker 인터페이스를 사용한 덕분에 UserDao와 ConnectionMaker의 구현 클래스들은 직접 연결되어 있지 않은 것처럼 보인다.
    • 하지만 UserDao 에서 new DConnectionMacker() 를 해주는 코드가 있음으로써 클래스 사이에 직접적인 관계가 있다.
    • UserDao가 NConnectionMaker를 사용하게 하려면 UserDao의 코드를 변경해야 한다.

    • 클래스 사이에 관계가 만들어진다는 것은 한 클래스가 인터페이스 없이 다른 클래스를 직접 사용한다는 뜻이다.
    • 오브젝트 사이의 관계는 런타임 시에 한쪽이 다른 오브젝트의 레퍼런스를 갖고 있는 방식으로 만들어진다.
  • 이 문제를 해결하려면 객체지향의 다형성이라는 특징을 이용하면 된다.
    • 특정 클래스를 알지 못하더라도 해당 클래스가 인터페이스를 구현했다면, 그 클래스의 인터페이스 타입을 사용할 수 있다.
    • UserDao가 DConnectionMacker를 사용하게 하려면 두 클래스 사이에 런타임 사용관계(=링크 =의존관계)를 맺어주면 된다.

    • ConnectionMaker의 구현 클래스를 선택하고, 선택한 클래스의 오브젝트를 생성하는 책임을 UserDao의 생성자가 아니라 UserDao의 클라이언트에게 넘기도록 수정한다.
  • 예제 코드

    인터페이스를 도입하고 클라이언트의 도움을 받는 방법은 상속을 사용하는 것에 비해서 훨씬 더 유연하다.

객체지향 설계와 프로그래밍 이론을 통해서 지금까지 적용한 벙법을 정리해보자 객체 지향 설계 원칙(SOLID)

원칙과 패턴

개방 폐쇄 원칙

개방 폐쇄 원칙(OCP, Open-Closed Principle)은 클래스나 모듈은 확장에는 열려 있어야 하고 변경에는 닫혀 있어야 한다는 원칙이다.

UserDao는 DB 연결 방법이라는 기능을 확장하는 데는 열려 있다. 동시에 UserDao 자신의 핵심 기능을 구현한 코드는 변화에 영향을 받지 않으므로 변경에는 닫혀 있다.

객체지향 설계 원칙(SOLID)

높은 응집도 낮은 결합도
  • 응집도가 높다는 것은 하나의 모듈, 클래스가 하나의 책임 또는 관심사에만 집중되어 있다는 뜻이다.
  • 높은 응집도는 클래스 레벨뿐 아니라 패키지, 컴포넌트, 모듈에 이르기까지 그 대상의 크기가 달라도 동일한 원리로 적용될 수 있다.
  • 응집도가 높다는 것은 변화가 일어날 때 해당 모듈에서 변하는 부분이 크다는 것으로 설명할 수도 있다.

처음에 개발한 초난감 DAO처럼 여러 관심사와 책임이 얽혀있는 복잡한 코드에서는 변경이 필요한 부분을 찾아내는 것도 번거로울뿐더러 변경으로 인한 오류를 발생시키지 않는지도 일일이 확인해야 한다.

반면, ConnectionMaker 인터페이스를 이용해 DB 연결 기능을 독립시킨 경우, DB 커넥션 풀을 활용하는 ConnectionMaker 구현 클래스를 새로 만들기만 하면 된다.

  • 낮은 결합도는 높은 응집도보다 더 민감한 원칙이다.
  • 결합도란 하나의 오브젝트에 변경이 일어날 때, 관계를 맺고 있는 다른 오브젝트에게 변화를 요구하는 정도이다.

ConnectionMaker 인터페이스의 도입으로 인해 DB 연결 기능을 구현한 클래스가 바뀌더라도 DAO의 코드는 변경될 필요가 없다.

ConnectionMaker의 클래스를 결정하는 책임을 DAO의 클라이언트로 분리한 덕분에 ConnectionMaker의 구현 클래스가 바뀌어도, DAO의 코드를 수정할 필요가 없다.

전략 패턴
  • 개선한 UserDtoTest, UserDao, ConnectionMaker 구조를 디자인 패턴의 시각으로 보면 전략 패턴에 해당한다고 볼 수 있다.
  • 전략 패턴은 디자인 패턴의 꽃이라고 불릴 만큼 다양하게 자주 사용되는 패턴이다.
  • 전략 패턴은 자신의 기능 맥락에서 필요에 따라 변경이 필요한 알고리즘(=전략)을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 클래스를 바꿔서 사용할 수 있게 하는 디자인 패턴이다.

UserDao는 전략 패턴의 컨텍스트에 해당한다. 컨텍스트는 자신의 기능을 수행하는데 필요한 기능 중 변경 가능한, DB 연결 방식이라는 알고리즘을 ConnectionMaker라는 인터페이스로 정의하고, 이를 구현한 클래스(=전략)를 바꿔가면서 사용할 수 있게 분리했다.

Reference

[토비 스프링] 사용자 DAO [토비의 스프링 vol.1]1장 오브젝트와 의존관계