스프링 공부 - 1.3, 1.4 정리

haaaalin·2022년 1월 24일
0
post-thumbnail

1.3 DAO의 확장

앞서 데이터 엑세스 로직과 DB 연결 방법에 관한 관심 사항을 각각 상하위 클래스로 분리시켰다. But, 아직 상속 관계를 사용했다는 것이 불편하다..

클래스의 분리

이제 상속 관계를 사용하던 것을 버리고, 그냥 서로 다른 두 개의 클래스로 완전히 독립시켜 버리자.

이렇게 했을 때의 문제점

  • UserDao 코드의 수정 없이 DB 커넥션 생성 기능을 변경할 방법 X ( simplecConnectionMaker 말고, 다른 클래스를 제작해 사용시, 당연히 변경해야 한다. )

    -> UserDao 의 코드를 제공해 주어야 한다는 것..

  • SimpleConnectionMaker 의 메소드 (메소드의 이름이 변경되면, UserDao 에 있는 add()get() 안에서 호출한 메소드들의 이름도 다 바꿔야 한다.)

  • DB 커넥션을 제공하는 클래스가 어떤 것인지를 UserDao 가 구체적으로 알고 있어야 함
    (중요 !!!!⭐⭐⭐)

인터페이스의 도입

클래스를 두 개로 분리시키면서도 저 문제점을 해결할 수 는 없을까?
-> 있다 !! 바로 추상화 (어떤 것들의 공통적인 성격을 뽑아내어 이를 따로 분리해내는 작업)

인터페이스를 사용하면 된다.

인터페이스는 위에서 언급한 3가지 문제점 중, 중요하다고 한 문제점을 아주 깔끔하게 해결해주는 방법 중 하나다.
인터페이스로 먼저 추상화해놓은 통로를 통해 직접 구현해놓은 클래스를 접근하기 때문이다.
단지 인터페이스만 알아도 원하는 기능을 사용할 수 있다.

✅ 결론적으로 UserDao 는 인터페이스의 메소드를 통해 알 수 있는 기능에만 관심을 가지면 되고, 그 기능을 어떻게 구현했는지 관심을 둘 필요가 없다.

public class UserDao {
	private ConnectionMaker connectionMaker;
    
    public UserDao() {
    	connectionMaker = new DConnectionMaker();
    }
    ...
}

그렇지만 아직도 UserDao 의 생성자 코드를 보면, 어떤 클래스의 오브젝트를 사용할지를 결정하는 부분이 제거되지 않았다.


👀 여기서 다시 짚고 가야 할 점

우리가 코드를 제공할 때에는 상세한 코드는 제공하지 않으려 한다.
즉, 코드를 제공 받은 회사 측에서 기능이나 방법을 변경하고 싶을 때, 우리가 제공하는 코드를 수정하지 않고, 추가로 확장시키는 방향으로 하도록 !!
-> UserDao 는 수정할 필요가 없게 만들어야 한다.

관계설정 책임의 분리

인터페이스를 도입해서까지 두 개의 관심(UserDaoConnectionMaker)을 완벽하게 분리했지만, 왜 계속 문제가 남아있는 걸끼 ❔

그 이유는 바로, UserDao 속에 어떤 ConnecionMaker 구현 클래스의 오브젝트를 이용할지를 결정하는 관심사항이 아직도 남아있기 때문이다.

저 관심사항을 분리해서 두기에 적절한 곳은? 클라이언트 오브젝트

🤔 클라이언트 오브젝트란?

한 오브젝트가 다른 오브젝트의 기능을 사용한다며, 사용되는 쪽이 사용하는 쪽에게 서비스를 제공하는 셈이다. 이때 사용하는 오브젝트를 클라이언트라고 부른다.


오브젝트 사이의 관계는 런타임 시에 한쪽이 다른 오브젝트의 레퍼런스를 갖고 있는 방식으로 만들어진다.

connectionMaker = new DConnectionMaker();

위 코드는 UserDao, DConnectionMaker 이 두 개의 오브젝트가 '사용' 이라는 관계를 맺게 해준다.


🧐 오브젝트 사이의 관계를 형성하기 위해 그 필요한 오브젝트를 UserDao 코드 내에서 만들 필요가 있을까?

💡 오브젝트는 메소드 파라미터 등을 이용해 전달할 수 있다. (외부에서 오브젝트를 생성해도 된다)

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

이렇게 외부에서 받을 수 있도록 만든다.

여기서 전 코드와 달라진 점!
DConnectionMaker 오브젝트와 관계를 맺는 책임을 원래 UserDao 의 생성자에서 오로지 졌지만, 이제는 외부에서(UserDao 의 클라이언트 오브젝트) 그 책임을 담당하고 있다.

클래스 사이의 관계 vs 오브젝트 사이의 관계

  • 클래스 사이의 관계: 한 클래스가 인터페이스 없이 다른 클래스를 직접 사용
  • 오브젝트 사이의 관계: 특정 클래스를 알지 못하더라도 해당 클래스가 구현한 인터페이스를 통해 그 클래스의 오브젝트를 받아서 사용

🔨 관계 설정 책임을 담당한 클라이언트 UserDaoTest가 추가된 구조

원칙과 패턴


1. 개방 폐쇄 원칙

객체지향 설계 원칙 중 하나인 이 원칙을 간단히 설명하자면, '클래스나 모듈은 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다' 라고 할 수 있다.

📁 객체지향 설계 원칙(SOLID)

  • SRP(The Single Responsibility Principle): 단일 책임 원칙
  • OCP(The Open Closed Principle): 개방 폐쇄 원칙
  • LSP(The Liskov Sbstitution Principle): 리스코프 치환 원칙
  • ISP(The Interface Segregation Principle): 인터페이스 분리 원칙
  • DIP(The Dependency Inversion Principle): 의존관계 역전 원칙

2. 높은 응집도와 낮은 결합도

'응집도가 높다' 라는 것은 하나의 모듈, 클래스가 하나의 책임 또는 관심사에만 집중되어 있다는 뜻이다. 변화가 일어날 때, 해당 모듈에서 변하는 부분이 크다는 것.

만약, 모듈의 일부부에만 변경이 일어나도 된다면, 어떤 부분이 바뀌어야 하는지, 일일이 확인해야 하고, 다른 바뀌지 않은 부분에는 영향이 미치는지 파악해야하는 부담이 생긴다.


'결합도가 낮다' 라는 것은 하나의 변경이 발생할 때, 다른 모듈과 객체들에 변경에 대한 요구가 전파되지 않는 상태를 말한다.

책임과 관심사가 다른 오브젝트 또는 모듈과는 느슨하게 연결된 형태를 유지하는 것이 바람직하다는 말씀

3. 전략 패턴

전략 패턴이란, 필요에 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고, 이를 구현한 구체적인 알고리즘 클래스를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴이다.



1.4 제어의 역전(IoC - Inversion of Control)

오브젝트 팩토리

앞서 짠 코드에는 또 문제점이 발견된다.
원래 UserDaoTestUserDao 의 기능이 잘 동작하는 지를 테스트하려고 만든 것인데, 테스트 진행 책임과, 다른 책임까지 떠맡고 있게 되었다. 이 두 가지 책임을 분리시키자!

이 책임을 분리시킬 팩토리를 만들자

🤔 팩토리란?
객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 오브젝트
이름에 걸맞게 그냥 오브젝트를 생성하는 공장이라고 보면 된다.

public class DaoFactory {
	public UserDao userDao() {
    	ConnectionMaker connnectionMaker = new DConnectionMaker();
        UserDao userDao = new UserDao(connectionMaker);
        // 객체의 생성방법 결정
        return userDao;
        // 그렇게 만들어진 객체 돌려주기
    }
}

설계도로서의 팩토리

UserDaoConnectionMaker 는 실질적인 로직을 담당하는 컴포넌트라면, DaoFactory 는 컴포넌트의 구조와 관계를 정의한 설계도 같은 역할을 한다고 볼 수 있다.

⭐⭐⭐⭐⭐
DaoFactory의 장점
애플리케이션의 컴포넌트 역할을 하는 오브젝트와 애플리케이션 구조를 결정하는 오브젝트를 분리 !!

제어권의 이전을 통한 제어관계 역전

제어의 역전이란, 간단히 프로그램의 제어 흐름 구조가 뒤바뀌는 것이라고 할 수 있다. 보통 제어의 흐름은, 모든 오브젝트가 능동적으로 자신이 사용할 클래스를 결정, 언제 어떻게 그 오브젝트를 만들지 스스로 제어한다.
하지만, 제어의 역전이란, 이 제어의 흐름을 거꾸로 뒤집는 것이다.

제어의 역전에서 오브젝트가 스스로 필요한 오브젝트를 생성하지도 못하고, 선택하지도 못한다. 심지어 자신조차도 어떻게 만들고 어떻게 사용되는지 모른다.

👀 라이브러리 vs 프레임워크
라이브러리와 프레임워크는 분명히 다른 개념이다.

  • 라이브러리: 라이브러리를 사용하는 애플리케이션 코드는 애플리케이션 흐름을 직접 제어한다.
  • 프레임워크: 거꾸로 애플리케이션 코드가 프레임워크에 의해 사용된다. 보통 프레임워크 위에 개발자가 개발한 클래스를 프레임워크가 진행되는 도중에 사용하도록 만든다.

📁 참고

  • 토비의 스프링 3.1 Vol.1 - 이일민
profile
한 걸음 한 걸음 쌓아가자😎

0개의 댓글