-
[Spring] 예제에 객체 지향 원리 적용해보기BackEnd/Spring 2022. 7. 27. 21:10
앞서 만든 예제는 client 코드가 구체클래스, 인터페이스 모두에 의존함!
그래서 구체클래스를 변경할 때 클라이언트도 함계 변경해서 DIP를 위반함
역할과 구현을 제대로 나눴다고 보기 어려움
DIP를 위반하지 않도록 인터페이스에만 의존하도록 의존관계를 변경하자
아래처럼 구체적인 RateDiscountPolicy() 정의하는 코드를 없애자
이 상태에서는 null pointer exception 발생함
누군가가 이 클라이언트 orderServiceImpl 객체에 DiscountPolicy의 구현 클래스를 주입시켜줘야함
AppConfig
: 애플리케이션의 전체 동작 방식을 구성(config)하기 위해, 구현 객체를 생성하고 연결하는 책임을 가지는
별도의 설정 클래스
1) 구현 객체를 생성함
2) 생성자를 통해서 주입함(연결)
- 인터페이스 선언만 해놓고 실체 구현체는 생성자로 받음
- MemoryMemberRepository를 의존하지 않고 인터페이스만 의존하게됨
- 어떤 구현객체가 들어올지 모르고 어떤 객체를 주입할지는 오직 AppConfig에서만 결정됨
MemberServiceImpl 구현 클래스
- 이로써 관심사가 분리되었음 -> 역할과 구현이 분리됨
- appConfig 객체는 memoryMemberRepository 객체를 생성하고 그 참조값을 memberServiceImpl을 생성하면서 생성자로 전달함
- impl 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 : DI(Dependency Injection) - 의존성 주입
OrderServiceImpl 구현 클래스
- 설계 변경으로 더이상 impl 객체가 fixdiscountPolicy를 의존하지 않음 -> 인터페이스만 의존
AppConfig 실행
AppConfig 리팩터링
- 각 impl에 new MemoryMemberRepository()가 두번 선언되어 있음 -> 중복
- 리팩토링을 이용하여 중복을 제거해보자
- ctrl + m을 누르면 인텔리제이에서 리팩토링 기능을 수행해줌(window)
- 리팩토링으로 중복이 제거되고, 애플리케이션의 전체 구성을 한눈에 확인할 수 있음
이제 여기서 할인정책을 고정에서(FixDiscouintPolicy) 정률할인 정책으로 바꾸려면 client 객체 바꾸는 일 없이
AppConfig에서 한줄만 바꾸면 됨
좋은 객체 지향 설계의 5가지 원칙의 적용
1. SRP 단일 책임 원칙
: 한 클래스는 하나의 책임만 가져야 한다.
- 이전 클라이언트 코드는 직접 구현 객체를 생성하고, 연결하고 , 실행하는 다양한 책임을 가짐
- AppConfig가 구현 객체를 생성하고 연결하는 책임, 클라이언트 객체를 실행하는 책임 으로 나눔
2. DIP 의존관계 역전 원칙
: 의존성 주입 -> 구체화가 아닌 추상화에 의존
- 이전 클라이언트 코드에는 DiscountPolicy 즉, 추상화 인터페이스에만 의존하는 것이 아니라
구체화 구현 클래스도 함께 의존했음(FixDIscountPolicy, RateDIscountPolicy)
- AppCofig가 객체를 대신 생성해서 클라이언트 코드에 의존관계를 주입한다.
3. OCP
: 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
- AppConfig가 의존관계를 Fix -> Rate로 변경하고 클라이언트 코드에 주입하면 클라이언트는 변경하지 않은 상태로
소프트웨어가 새롭게 확장될 수 있음.
- 그리고 사용 영역의 변경에는 닫혀있게 됨
IoC, DI , 그리고 컨테이너
IoC
- 프로그램에 대한 제어 흐름에 대한 권한은 모두 AppConfig가 가짐
- 제어의 역전(IoC)란 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것
DI
정적인 클래스 의존 관계
- 클래스가 사용하는 import 코드만 보고 의존관계 쉽게 판단 가능(애플리케이션 실행x)
- OrderServiceImpl은 MemberRepository, DiscountPolicy에 의존함
하지만 !!! 실제 어떤 객체가 주입될지는 모름
동적인 클래스 의존 관계
- 애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서
클라이언트와 서버의 실제 의존관계가 연결 되는 것을 의존관계 주입이라고 함
따라서 AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해주는 것을 IoC컨테이너 또는 DI컨테이너라고 함
스프링으로 전환해보기
- AppConfig에 설정을 구성한다는 뜻의 @Configuration
- 각 메서드에 @Bean 붙이면 스프링 컨테이너에 스프링 빈으로 등록됨
- 두 코드를 실행하면 스프링 관련 로그가 실행되면서 기존과 동일한 결과가 출력됨
스프링 컨테이너란?
- ApplicationContext를 스프링 컨테이너라고 함
- 이전에는 개발자가 직접 AppConfig 사용해서 DI했음 -> 스프링 컨테이너를 사용하자!
AppConfig appConfig = new AppConfig(); MemberService memberService = appConfig.memberService(); OrderService orderService = appConfig.orderService();
- @Configuration이 붙은 AppConfig를 설정 정보로 사용함
- @Bean이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록함 -> 이렇게 컨테이너에 등록된
객체를 스프링 빈이라고 한다.
- @Bean 이 붙은 메서드의 명 = 스프링 빈의 이름
- 스프링 빈은 applicationContext.getBean()로 찾을 수 있음
'BackEnd > Spring' 카테고리의 다른 글
[Spring] 싱글톤 컨테이너 (0) 2022.08.02 [Spring] 예제 만들기 (0) 2022.07.22