오브젝트와 의존관계

디디·2020년 9월 15일
0
post-thumbnail

1.2 Dao의 분리

관심사 분리

시간이 지남에 따라 모든건 변한다. 개발자는 항상 미래에 대비해야 한다.

객체지향 기술이 변화에 효과적으로 대처할 수 있는 기술이다.

적은 변화와 변화에 대한 검증을 빠르게 하는 개발자.

분리와 확장을 고려한 설계가 필요하다

DB가 바뀌었을때, 웹 화면의 레이아웃이 바뀌었을때, 등등..

변화는 대체로 한 가지 관심에 대해 일어나지만 그에 따른 작업은 집중되지 않는 경우가 많다. → 변화에 대한 변경이 한 가지에 집중해서 일어나야 한다. 즉 관심이 같은 것 끼리는 모으고 관심이 다른 것은 분리해낸다.

관심사 분리

커넥션 만들기 추출

  • DB Connection 가져오기 관심
  • 사용자 등록 SQL 실행 관심
  • Resource Close 하는 관심

변경사항에 대한 검증: 리팩토링과 테스트

두번째 실행부터 예외가 터진다. PK이기 때문에. → DB를 삭제해주고 다시실행하면 동작한다.

이런 과정을 리팩토링이라고 하고, 메소드 추출 기법이라고 한다.

N사와 D사가 서로 다른 DB를 사용한다면, DB커넥션을 가져오는 방법 또한 변경될 가능성이 있다. UserDao 소스코드를 제공하지 않는다면 어떻게 해야할까.

상속을 통한 확장

어떻게 데이터를 등록하고 가져올지에 대한 관심 → UserDao

어떻게 Connection을 가져올지에 대한 관심 → NUserDao, DUserDao

로 분리한다. 이는 변경이 용이하다 뿐만 아니라 손쉽게 확장된다라고 볼 수 있다.

이를 템플릿 메소드 패턴이라고 한다.

서브클래스에서 구체적인 오브젝트 생성 방법을 결정하게 하는것을 팩토리 메소드 패턴이라고 부르기도 한다.

상속을 사용했다는 단점. 이미 UserDao가 상속중이라면? 상속은 상하위 클래스의 관계가 너무 밀접하다.

슈퍼클래스의 변화가 서브클래스의 변화를 초래하게 됨. DB 커넥션 코드를 다른 곳에서 못쓰게됨 (서브클래스를 다른데서 못씀)

1.3 DAO의 확장

데이터 엑세스 로직 → DB 커넥션 분리

변화의 성격이 다르다면. 변경되면 안된다.

  1. 메소드 분리를 통해 관심사를 분리했다
  2. 상속을 통해 상하위 클래스로 분리했다
  3. 아예 독립된 클래스로 만들어보자.

SimpleConnectionMaker방식의 문제점

직접 UserDao에서 생성해서 사용하므로 N사 D사에 따라 다른 클래스의 다른 메서드의 다른 타입을 사용할 수 없게 된다.

  1. D사 ConnectionMaker에서는 메서드명이 openConnection()이라면?
  2. UserDao가 SimpleConnectionMaker에 대한 정보를 알아야 한다는 점 → N사에서 다른 클래스를 쓰면 다시 고쳐야함

이런 문제는 Connection을 가져올때 너무 많이 알고있기 때문

인터페이스 도입

ConnectionMaker → NConnectionMaker, DConnectionMaker

UserDao에서 NConnectionMaker()를 생성해서 사용하는 방식 → 아직도 Connection에 대한 지식이 남아있다.

관계설정 책임의 분리

왜 UserDao가 인터페이스가 아닌 구현체를 알고있어야 하는가?

→ 아직도 어떤 ConnectionMaker를 사용할지를 결정하는 관계 설정의 대한 관심을 가지고 있다.

즉 UserDao와 ConnectionMaker 특정 구현체와의 관계를 설정해주는 것에 관한 관심이 남아있다.

클라이언트는 사용하는 오브젝트. 사용되는 오브젝트는 서비스. 클라이언트 오브젝트는 관계를 결정해주는 기능(책임)을 맡기 적절한 곳이다.

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

외부에서 만들어 준 것을 가져오는 방식. 파라미터로 제공받은 오브젝트는 이용만 하면된다. 다른건 신경 x

클래스간 관계는 코드로 드러난다. 오브젝트간 관계는 런타임에 드러난다. 코드에서는 특정 클래스를 모르더라도 사런타임에 들어오므로 사용가능하다 → 다형성을 통해

UserDao 클라이언트는 ConnectionMaker 구현 클래스를 선택하고, 생성해서, 연결해준다. → Main 메소드

원칙과 패턴

SOLID

  • 단일 책임 원칙 : 하나의 클래스는 단 한가지의 변경이유만을 가져야한다. → 응집도 (책임은 변경의 이유)
  • 개방 폐쇄 원칙 : 클래스나 모듈은 확장에 열려있고 변경에는 닫혀 있어야 한다. → 변경을 예측하라. 첫번째 총알은 그냥맞고 그다음부터 적용한다.
  • 리스코프 치환 원칙 : A를 B로 모두 치환 가능하면 A는 B의 하위타입이다. → 인터페이스의 관점으로 확대
  • 인터페이스 분리 원칙: 클라이언트가 자신이 이용하지 않는 메서드에 대해 의존하지 않아야 한다는 원칙. 즉 메소드의 변경이 이 메소드를 사용하지 않는 클라이언트에 영향을 줄 때 인터페이스로 분리해야한다.
  • 의존관계 역전 원칙 : 상위 수준 모듈은 하위수준 모듈에 의존하면 안된다. 둘 모두 추상화에 의존해야한다.

높은 응집도와 낮은 결합도

  • 응집도가 높다는 것. 변경에 대해 모듈에서 변화는 부분이 크다는 것. 여러 관심사와 책임이 얽혀있는 코드는 응집도가 낮다.
  • 결합도가 낮다는 것. 책임과 관심사가 다른 오브젝트와는 느슨한 연결 형태를 유지한다는 것. 관계를 유지할땐 최소한의 방법으로 간접적으로. 결합도란 하나의 오브젝트의 변화가 일어날 때 관계를 맺고있는 오브젝트에게 변화를 요구하는 정도 라고 보면 된다.

전략 패턴

전략 패턴은 자신의 기능 맥락에서 필요에ㄷ 따라 변경이 필요한 알고리즘을 인터페이스를 통해 통째로 외부로 분리시키고 구현체를 필요에 따라 바꿔서 사용할 수 있게 하는 디자인 패턴.

public class UserDao {
	private ConnectionMaker connectionMaker;
	public UserDao(ConnectionMaker connectionMaker){
		this.connectionMaker = connectionMaker;
	}
	
	public User get(String id){
		Connection connection = connectionmaker.getConnection();
		...
	}
}
		

DB연결 방식이라는 알고리즘을 ConnectionMaker 인터페이스로 정의하고 구현체를 바꿔가면서 사용할 수 있게 분리했다.

또한 메인메서드와 같은 클라이언트의 필요성 또한 설명해준다. 전략 패턴을 적용 시 클라이언트는 컨텍스트가 사용할 전략을 컨텍스트의 생성자 등을 통해 제공해준다.

1.4 제어의 역전(IoC)

Main 메서드는 어떤 ConnectionMaker 구현체를 사용할지를 결정하는 기능을 떠맡았다.

팩토리

객체의 생성 방법을 결정하고 그렇게 만들어진 오브젝트를 돌려주는 역할을 하는 클래스를 만들어보자. 이런 오브젝트를 흔히 팩토리라고 부른다.

컴포넌트의 구조와 관계를 정의한 설계도 같은 역할을 한다.

제어의 역전이라는 건 프로그램의 제어 흐름 구조가 뒤바뀌는 것.

일반적으로 메인 → 오브젝트 생성 → 메소드 호출 → 메소드 안에서 오브젝트 생성 → 메소드 안 메소드 호출

이런 흐름에서 각 오브젝트는 프로그램 흐름을 결정하거나 사용할 오브젝트를 구성하는 작업에 능동적으로 참여한다. 즉 모든 종류의 작업을 사용하는 쪽에서 제어하는 구조. 생성해서 사용

제어의 역전에서는 오브젝트가 자신이 사용할 오브젝트를 스스로 선택하지 않는다. 당연히 생성하지도 않는다. 모든 제어 권한을 다른 대상에게 위임한다. 모든 오브젝트는 위임받은 오브젝트에 의해 결정되고 만들어진다.

프레임워크는 라이브러리가 아니다. 프레임워크는 흐름을 주도하고 개발자가 만든 코드를 사용하는 방식으로 동작한다.

기존 ConnectionMaker 구현 클래스를 결정하고 만드는 제어권은 UserDao에 있었으나 DaoFactory로 옮겨갔다. 자신이 어떤 ConnectionMaker 구현 클래스를 사용할 지에 대한 권한을 DaoFactory에게 넘김으로써 UserDao는 수동적인 존재가 되었다.

제어의 역전에서는 컴포넌트의 생성과 관계설정, 사용, 생명주기 관리 등을 관장하는 존재가 필요하다. DaoFactory는 가장 단순한 IoC 컨테이너로 불릴 수 있다.

1.5 스프링의 IoC

스프링에서 빈의 생성과 관계설정 같은 제어를 담당하는 IoC 오브젝트를 빈 팩토리라고 부른다. (어플리케이션 컨텍스트라고도 한다.)

우리가 작성한 DaoFactory가 빈 팩토리의 설정 정보가 된다.

@Bean 메소드 이름은 빈 이름이 된다. 이름이 필요한 이유는 생성 방식이 다르게 될 수 있기 때문.

애플리케이션 컨텍스트 동작방식

DaoFactory가 Dao 오브젝트를 생성하고 관계를 맺어주는 녀석이였다면, 애플리케이션 컨텍스트는 IoC를 적용해서 관리할 모든 오브젝트에 대한 생성과 관계설정을 담당한다.

애플리케이션 컨텍스트는 @Configuration을 통해 설정정보를 등록해놓고 @Bean을 통해 메소드 이름을 가져와 빈 목록을 만들어 둔다.

애플리케이션 컨텍스트를 사용하는 이유

  • 클라이언트가 구체적인 팩토리 클래스를 알 필요가 없다 → 좀 더 일관된 방법으로 빈을 가져올 수 있다.
  • 종합 IoC 서비스를 제공해준다 → 오브젝트가 만들어지는 방식을 다양하게. 효과적으로 오브젝트를 활용
  • 빈을 검색하는 기능 제공

스프링 용어 정리

  • 빈: IoC방식으로 관리하는 오브젝트
  • 빈 팩토리 : IoC 핵심 컨테이너. 빈 등록, 생성, 조회, 관리
  • 애플리케이션 컨텍스트 : 빈 팩토리를 확장한 IoC 컨테이너. 스프링의 각종 부가 서비스를 제공한다
  • 설정정보 설정 메타정보 : 애플리케이션 컨텍스트가 IoC를 적용하기 위해 사용하는 메타정보.
  • 컨테이너 : 다 비슷
  • 스프링

싱글톤 레지스트리와 오브젝트 스코프

애플리케이션 컨텍스트는 싱글톤을 저장하고 관리하는 싱글톤 레지스트리. 정상적인 자바 객체를 싱글톤으로 활용하게 해준다.

싱글톤과 오브젝트의 상태

멀티스레드 환경에선 상태관리에 주의를 기울여야 한다. 서비스 형태의 오브젝트로 사용되는 경우 무상태 방식으로 만들어져야 한다. 필드가 아닌 파라미터, 로컬변수, 리턴값 등을 활용해야한다.

물론 필드가 싱글톤이거나 읽기전용이라면 가능하다. 한번 초기화해주고 나면 수정되지 않기 때문.

빈의 스코프

스프링 빈의 기본 스코프는 싱글톤이다. 강제로 제거하지 않는 한 컨테이너가 존재하는 동안 계속 유지된다.

(프로토타입 스코프는 매번 새로 만들어준다) (Request Scope가 있다) (Session 스코프도 있다)

의존관계 주입(DI)

IoC는 일반적인 개념. 객체를 생성하고 관계를 맺어주는 등의 작업을 담당하는 기능을 일반화한것이 IoC 컨테이너

IoC는 너무 포괄적인 개념이라, 스프링은 IoC 방식의 핵심인 의존관계 주입 이라는 의도가 명확히 드러나는 이름을 사용하기 시작했다.

DI는 오브젝트를 외부로부터 주입받아 다이나믹한 의존 관계를 만드는게 핵심

의존관계

A가 B에 의존한다면, B가 변하면 A에 영향을 미친다. A가 변해도 B는 영향을 받지 않는다.

UserDao는 ConnectionMaker에 의존한다. ConnectionMaker 인터페이스가 변경된다면 UserDao는 영향을 받을것이다. 그러나 ConnectionMaker는 인터페이스임. 추상클래스 NConnectionMaker가 변경되더라도 UserDao엔 직접적인 영향을 미치지 않는다. → 인터페이스에 대해서만 의존관계를 만든다면, 실제 구현클래스와의 관계는 느슨해지고, 변화에영향을 덜받게 된다. 결합도가 낮아진다.

런타임 시에 의존관계를 맺는 대상을 의존 오브젝트라고 말한다.

의존관계 주입이란

  • 클래스나 코드에서 런타임 시 의존관계가 드러나지 않는다. 인터페이스에만 의존하고 있는다.
  • 런타임 시 의존관계는 컨테이너나 팩토리같은 제 3자가 결정한다.
  • 외부로부터 의존 오브젝트를 주입받음으로써 의존관계가 만들어진다.

DI의 핵심은 관계를 맺도록 도와주는 제 3자가 있다는점. → 관계설정 책임을 분리해낸 오브젝트. DaoFactory나 IoC 컨테이너등.

DaFactory는 런타임 의존관계 설정해주는 역할, IoC 방식의 오브젝트 생성 초기화 제공.

→ DI를 담당하는 컨테이너. DaoFactory는 일종의 IoC/DI 컨테이너

DI는 자신이 사용할 오브젝트에 대한 선택, 생성제어를 외부로 넘기고 자신은 수동적으로 주입받은 오브젝트를 사용한다. IoC의 개념과 거의 동일하다.

의존관계 검색

자신이 필요한 의존 오브젝트를 능동적으로 찾는다. 어떤 클래스가 아닌.

getBean을 통해 의존관계를 검색하여 가져온다.

의존관계 검색은 차이점이 주입받는 녀석이 빈일 필요는 없다.

부가기능 추가

Proxy처럼 사용

메소드를 이용한 DI

profile
식빵 고양이 디디

0개의 댓글