토비의스프링vol.1

[토비의 스프링vol.1 3장] 템플릿

ohyujeong 2022. 10. 31. 21:32

3장 템플릿

개방 폐쇄 원칙: 확장에는 자유롭게 열려 있고 변경에는 굳게 닫혀있다.

템플릿 : 바뀌는 성질이 다른 코드 중에서 변경이 거의 일어나지 않으며

일정한 패턴으로 유지되는 특성을 가진 부분을

자유롭게 변경되는 성질을 가진 부분으로부터 독립시켜서

효과적으로 활용할 수 있는 방법이다.

분리와 재사용을 위한 디자인 패턴 적용

내부 클래스

클래스 분리와 DI

템플릿과 콜백

정리

예외처리

예외처리 필요성 : 예외가 발생했을 경우에도 사용한 리소스를 반드시 반환해야 한다.

  • try/catch/finally

    코드의 복잡도가 너무 높다.

    모든 메소드마다 반복된다.

분리와 재사용을 위한 디자인 패턴 적용

  • 변하는 부분을 변하지 않는 나머지 코드에서 분리

변하는 부분 : 쿼리 수행

변하지 않는 부분 : try/catch/finally 로 감싼 부분

방법 1. 메소드 추출

  • 변하는 부분을 메소드로 추출한다.

문제 : 분리된 메소드가 재사용 되어야 하는데, 변하지 않는 남은 메소드가 재사용(반복)되고, 변하는 부분인 분리된 메소드는 재사용이 안 되고 매번 새롭게 만들어야 함

방법 2. 템플릿 메소드 패턴 적용

  • 상속을 통해 기능을 확장해서 사용한다.
  • 변하지 않는 부분(슈퍼클래스), 변하는 부분(추상클래스) 정의
  • 서브클래스에서 오버라이드 하여 새롭게 정의해 사용

문제 : DAO 로직마다 상속을 통해 새로운 클래스 만들어야 함

확장구조가 이미 클래스를 설계하는 시점에 고정됨 -> 컴파일 시점에 관계 결정, 유연성 떨어짐

방법 3. 전략 패턴 적용

  • 오브젝트를 아예 둘로 분리, 클래스 레벨에서는 인터페이스 통해서만 의존
  • 변하는 부분을 별도의 클래스로 만들어, 추상화된 인터페이스를 통해 위임
  • 변하는 부분 전략, 변하지 않는 부분 컨텍스트

문제 : 전략 패턴은 필요에 따라 컨텍스트는 그대로 유지(OCP폐쇄원칙), 전략은 바꿔 쓸 수 있음(OCP 페쇄원칙)

컨텍스트 안에서 구체적인 전략 클래스인 DeleteAllStatment사용하면, 인터페이스 뿐만 아니라 특정 구현 클래스를 직접 알고 있음으로 전략패턴, OCP에 들어맞지 않음

DI 적용을 위한 클라이언트/컨텍스트 분리

  • 컨텍스트를 사용하는 Client가 전략을 결정해야 함
  • 클라이언트가 하나의 전략을 오브젝트로 만들어서 컨텍스트에 전달

내부 클래스

로컬 클래스

위 코드의 문제

  • DAO메소드마다 새로운 StatementStrategy 구현 클래스 만들어야 함
  • DAO메소드에서 전달할 부가 정보 있는 경우, 이를 위해 오브젝트를 전달받는 생성자와 저장해둘 인스턴스 변수 만들어야 함

해결

특정 메서드에서만 사용되는 전략 클래스를 UserDao 안에 내부 클래스로 정의


장점 : 로컬 클래스는 내부 클래스이기 때문에 자신이 선언된 곳의 정보에 접근 가능
-> 번거롭게 생성자를 통해 User 오브젝트 전달하지 않아도 됨

익명 내부 클래스

  • 선언과 동시에 오브젝트 생성
  • 사용하면 클래스 이름도 제거 가능
  • 클래스 밖 변수는 final 붙어 있어야 사용 가능

클래스 분리와 DI

전략 패턴 구조

UserDao 메소드 : 클라이언트

익명 내부 클래스 : 개별 전략

jdbcContextWithStatmentStrategy() 메소드 : 컨텍스트

jdbcContextWithStatmentStrategy() 다른 UserDao 밖으로 독립시켜 다른 DAO에서도 사용 가능하게 만들자

클래스 분리

  • JDBC 작업 흐름을 분리하여 JdbcContext 클래스 만듦


UserDao -> DataSource


UserDao -> JdbcContext -> DataSource

  • UserDao에서는 커넥션 말고 쿼리를 작성하고 실행만 함
  • JdbcContext가 DataSource에 의존하고 있으므로, DataSource 타입 빈을 DI 받을 수 있게 해야 함

스프링 DI는 인터페이스 사이에 두고 의존 클래스 바꿔서 사용하는 게 목족


JdbcContext는 구체클래스, DataSource 인터페이스

인터페이스를 두지 않아도 괜찮을까?

  • 스프링의 DI는 넓게 보자면 객체의 생성과 관계설정에 대한 제어권한을 오브젝트에서 제거하고 외부로 위임했다는 IoC 개념 포괄
  • JdbcContext 를 스프링을 이용해 UserDao 객체에서 사용하게 주입했다는 건 DI 의 기본

DI 구조로 만든 이유

  • JdbcContext 가 싱글톤으로 등록돼서 여러 오브젝트에서 공유해 사용될 수 있음
  • JdbcContext 가 DI를 통해 다른 빈에 의존하고 있기 때문이다.
    • DataSource 오브젝트 주입 받음, DI를 위해서는 주입되는,하는 오브젝트 양쪽이 스프링 빈으로 등록되어야 함
    • 스프링이 생성하고 관리하는 IoC 대상이어야 DI에 참여 가능


스프링 DI
장점 : 오브젝트의 의존관계가 설정파일에 명확하게 드러남
단점 : 구체적인 클래스와의 관계 직접 노출

수동 DI

장점 : 관계와 DI 전략 외부에 노출 X

단점 : 싱글톤으로 못만듦

템플릿과 콜백

템플릿 : 전략패턴의 컨텍스트 (변하지 않는 부분)

콜백 : 익명 내부 클래스로 만들어지는 오브젝트(변하는 부분)

 

메소드 레벨에서 일어나는 DI

  • 클라이언트가 템플릿 메소드를 호출하면서 콜백 오브젝트를 전달하는 것

템플릿/콜백 패턴 특징

  • 매번 메소드 단위로 사용할 오브젝트를 새롭게 전달받음
  • 콜백 오브젝트가 내부 클래스로서 자신을 생성한 클라이언트 메소드 내의 정보 직접 참조
  • 클라이언트와 콜백이 강하게 결합

일반 DI

  • 템플릿에 인스턴스 변수 만들어두고 사용할 의존 오브젝트를 수정자 메소드로 받아서 사용

 

정리

  • 일정한 작업 흐름이 반복되면서, 일부 기능만 바뀌면 바뀌지 않는 부분 컨텍스트, 바뀌는 부분 전략으로 만들어 인터페이스를 통해 전략을 변경할 수 있도록 전략패턴 적용하여 구성
  • 여러 가지 전략 사용해야 하면, 컨텍스트 이용하는 클라이언트가 직접 전략 정의하고 제공
  • 클라이언트 메소드 안에 익명 내부 클래스 사용하여 전략 오브젝트 구현하면 코드 간결, 메소드 정보 직접 사용할 수 있어 편리
  • 컨텍스트가 하나 이상의 클라이언트 오브젝트에서 사용되면 클래스 분리해서 공유함
  • 컨텍스트는 빈으로 등록해서 DI 받거나, 수동으로 DI 하는 두 가지 방법
  • 템플릿/콜백 패턴은 컨텍스트 호출과 동시에 전략 DI를 수행하는 방식
  • 콜백 코드에도 일정한 패턴 반복되면 콜백을 템플릿에 넣고 재활용 함