Replies: 2 comments 2 replies
-
|
오.. 빠르네요 |
Beta Was this translation helpful? Give feedback.
1 reply
-
|
제일 먼저 올렸네요 |
Beta Was this translation helpful? Give feedback.
1 reply
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
3_스프링 핵심 원리 이해 2 - 객체 지향 원리 적용
새로운 할인 정책 개발
새로운 할인 정책을 개발하여 적용시키기로 하자
기존의 정액 할인 정책에서 정율 할인 정책으로의 변경이 필요함
RateDiscountPolicy파일 추가테스트 작성을 위해 jUnit을 이용한다.
이때, 단축키 command + shift + T 하면 jUnit 테스트 파일을 쉽게 생성 하도록 도와주는 창이 뜬다.
RateDiscountPolicyTest파일⭐
Assertions.assertThat()구문에서static import하여 코드를 변경하는 것이 좋음맥에서는 option + enter 단축키를 통해 static import로 바꾸면 코드가 간결해진다.
새로운 할인 정책 적용과 문제점
위에서는 새로운 할인 정책인 정률 할인 정책에 관련된 파일을 생성했다.
위에서 추가한 할인 정책을 적용 시키기 위해 코드를 수정해야 한다.
OrderServiceImpl내의 코드를 변경위의 코드에서의 문제점 ?
OrderServiceImpl에서 추상(인터페이스)인DiscountPolicy뿐만아니라 구체(구현)클래스인FixDiscountPolicy나RateDiscountPolicy를 의존하고 있다.OrderServiceImpl내의 코드를 변경해야하기 때문에 OCP를 위반하고 있다.기대했던 의존 관계

OrderServiceImpl이 단순히 인터페이스인DiscountPolicy만을 의존한다고 생각했다.실제로는
OrderServiceImpl이DiscountPolicy인터페이스 뿐만 아니라FixDiscountPolicy인 구체 클래스도 함께 의존하고 있다. -> DIP 위반FixDiscountPolicy를RateDiscountPolicy로 변경하는 순간OrderServiceImpl의 소스 코드도 함께 변경해야 한다. (위의 코드 참고) -> OCP 위반✔️ 문제 해결 방법
이 문제를 해결하려면 누군가 클라이언트인
OrderServiceImpl에DiscountPolicy의 구현 객체를 대신 생성하고 주입해주어야 한다.이렇게하면, 인터페이스에만 의존하게 된다.
하지만 구현체가 존재하지 않아서 실제 실행을 해보면 NPE(Null Pointer Exception)이 발생한다.
-> 누군가 클라이언트인
OrderServiceImpl에DiscountPolicy의 구현 객체를 대신 생성하고 주입해주어야 한다.관심사의 분리
'관심사의 분리'란 ?
AppConfig가 등장한다.AppConfig의 등장
애플리케이션의 전체 동작 방식을 구성하기 위해, 구현 객체를 생성하고, 연결하는 별도의 설정 클래스이다.
AppConfig파일 생성이 파일에서는 애플리케이션에서 필요한 구현 객체를 생성한다.
MemberServiceImplMemoryMemberRepositoryOrderServiceImplFixDiscountPolicy그리고 객체 인스턴스의 참조를 생성자를 통해 주입한다.
MemberServiceImpl->MemoryMemberRepositoryOrderServiceImpl->MemoryMemberRepository,FixDiscountPolicy❗ 현재
MemberServiceImpl내의 문제점현재 코드에서는
MemberServiceImpl이MemoryMemberRepository를 의존하고 있다는 문제점이 존재한다.구현체를 의존하고 있는 것은 좋지 않기 때문에 설계를 변경한다.
변경 후 코드에서는
MemberServiceImpl이MemoryMemberRepository를 의존하지 않고 있다. 단지MemoryRepository인터페이스에만 의존하고 있다.이 경우,
MemberServiceImpl의 입장에서는 어떤 객체가 주입될지 모른다. -> 자신의 역할만을 충실하게 잘 해내고 있다 !MemberServiceImpl의 생성자를 통해서 어떤 객체를 주입할 지는 오직 외부인AppConfig에서 결정된다. ->AppConfig가 배역을 책임지는 역할만을 충실히 하고 있다 !이로써, 객체의 생성과 연결은
AppConfig가 담당하게 되었다.DIP가 완성되었다.
MemberServiceImpl은 구현체가 아닌, 추상 인터페이스인MemberRepository에만 의존하면 된다.DIP를 잠깐 잊었을까봐 ... 🥲
❗ 현재
OrderServiceImpl내의 문제점현재 코드에서는
OrderServiceImpl이MemoryMemberRepository와FixDiscountPolicy를 의존하고 있다.변경 후 코드에서는
OrderServiceImpl에서 더 이상MemoryMemberRepository와FixDiscountPolicy를 의존하지 않고, 인터페이스인MemberRepository와DiscountPolicy만 의존하고 있다.이 경우,
OrderServiceImpl입장에서는 생성자를 통해 어떤 구현 객체가 들어올 지는 알 수 없다. -> 자신의 역할만을 충실하게 잘 해내고 있다 !!!OrderServiceImpl의 생성자를 통해서 어떤 객체를 구현할 지는 오직 외부인AppConfig에서 결정한다. ->AppConfig가 배역을 책임지는 역할만을 충실히 하고 있다 !!!AppConfig 실행을 위해 변경해야하는 부분
MemberApp코드 내 수정기존 코드를 주석처리하고, 코드를 추가한다.
OrderApp코드 내 수정Test 코드에서의 수정
MemberServiceTest내의 코드 수정OrderServiceTest내의 코드 수정AppConfig 리팩터링
AppConfig파일을 리팩토링 한다.역할과 구현이 잘 분리되어있는 그림을 기대하는데, 현재
AppConfig내의 코드에서는 그렇게 보이지 않는다.위의 그림처럼 역할들을 드러나게 코드를 리팩터링하는 것이 좋다.
이렇게 리팩터링하면 나중에 구현체를 변경해야할 일이 생겼을 때 그 부분의 코드만 변경해주면 된다.
새로운 구조와 할인 정책 적용
정액 할인 정책 -> 정률 할인 정책으로 변경해보자.
위에서의
AppConfig의 등장덕분에 애플리케이션이 사용 영역과 구성하는 영역으로 분리되었다.사용과 구성의 분리

할인 정책의 변경

할인 정책의 변경을 위해 어떤 부분만 변경하면 되는가 ?
구성 영역의 코드만 변경하면 된다.
FixDiscountPolicy로 구현체를 설정했던 것을RateDiscountPolicy로만 변경하면 된다.사용 영역에 있는 코드는 전혀 손댈 필요가 없다 !
-> 이로써 OCP, DIP를 모두 만족시키는 코드로 변경되었다.
전체 흐름 정리
새로운 할인 정책 개발
새로운 정률 할인 정책 코드를 추가로 개발하는 것 자체에는 아무런 문제 X
새로운 할인 정책 적용과 문제점
정률 할인 정책으로 적용시키려고 하니, 클라이언트 코드인
OrderServiceImpl(주문 서비스 구현체)를 함께 변경해야 했다.왜냐하면
OrderServiceImpl에서 인터페이스인DiscountPolicy뿐만아니라, 구체 클래스인FixDiscountPolicy도 함께 의존하고 있었기 때문이다. (클라이언트 코드인 주문 서비스 구현테가 너무 많은 역할을 하고 있었던 것)관심사의 분리
AppConfig의 등장
애플리케이션의 전체 동작 방식을 구성하기 위해 구현 객체를 생성하고 연결하는 책임을 가진다.
이로 인해 클라이언트 객체는 자신의 역할을 실행하는 것에만 집중한다. 권한이 줄어들었다. (= 책임이 명확해짐)
AppConfig 리팩터링
역할과 구현을 명확하게 분리했다.
역할이 잘 들어날 수 있게 하였고 중복을 제거하였다.
새로운 구조와 할인 정책 적용
정액 할인 정책을 정률 할인 정책으로 변경하였다.
AppConfig의 등장으로 애플리케이션이 크게 사용 영역과, 객체를 생성하고 구성하는 영역으로 분리했다.
할인 정책을 변경하더라도 AppConfig 내의 구성 영역만 변경하면 되고, 사용 영역의 코드는 변경할 필요가 없다.
좋은 객체 지향 설계의 5가지 원칙의 적용
SRP 단일 책임 원칙
한 클래스는 하나의 책임만 가져야 한다.
AppConfigDIP 의존 관계 원칙
추상화에 의존해야지 구체화에 의존하면 안된다.
OCP
소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀 있어야 한다.
IoC, DI 그리고 컨테이너
제어의 역전 Ioc(Inversion of Control)
AppConfig의 등장으로 구현 객체는 자신의 로직을 실행하는 역할만을 담당하게 되었고,AppConfig는 제어의 흐름을 담당한다.OrderServiceImpl은 필요한 인터페이스들을 호출하지만 어떤 구현 객체들이 실행될지는 모른다.프레임워크 VS 라이브러리
의존 관계 주입 DI(Dependency Injection)
정적인 클래스 의존 관계
import코드만 보고서도 의존관계를 쉽게 판단할 수 있다. (정적인 의존 관계는 애플리케이션을 실행하지 않아도 분석 가능)OrderServiceImpl은MemberRepository와DiscountPolicy에 의존한다는 것을 알 수 있다.OrderServiceImpl에 주입될 지 알 수 없다.동적인 객체 인스턴스 의존 관계
객체 다이어그램
IoC 컨테이너, DI 컨테이너
AppConfig처럼 객체를 생성하고 관리하면서 의존관계를 연결해주는 것을 IoC 컨테이너 또는 DI 컨테이너라고 한다.스프링으로 전환하기
순수한 자바로만 잤던 코드를 스프링을 이용하도록 코드를 변경한다.
AppConfig코드 변경MemberApp에 스프링을 적용하기 위해 코드 변경OrderApp에 스프링을 적용하기 위해 코드 변경ApplicationContext를 스프링 컨테이너라고 한다.ApplicationContext가Bean이란 것을 모두 관리해준다.AppConfig를 사용해서 직접 객체를 생성하고 DI(의존성 주입)를 했지만, 이제부터는 스프링 컨테이너를 통해서 사용한다.@Configuration이 붙은AppConfig를 설정 정보로 사용한다.@Bean이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다. 이렇게 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 한다.@Bean이 붙은 메서드 명을 스프링 빈의 이름으로 사용한다.AppConfig를 사용하여 직접 조회했지만, 이제는 스프링 컨테이너를 통해 스프링 빈(객체)를 찾아야한다.applicationContext.getBean()메서드를 통해 찾을 수 있다.-> 스프링 컨테이너를 생성해줌으로써
@Bean붙은 것들은 스프링 컨테이너에 객체 생성한 것을 다 집어 넣어서 관리해준다.-> getBean 메소드는 2개의 인자를 필요로 하는데, 첫번째 인자는 메소드 이름이고 두번쨰 인자는 타입이다.
AppConfig도 직접 만들고 자바 코드로 모든 것을 했지만, 이제부터는 Spring에게 환경 정보를 던져주고 찾을 때는 스프링 컨테이너를 통해서 가져온다.Beta Was this translation helpful? Give feedback.
All reactions