[핵심원리][3-1] 스프링 핵심 원리 이해2 - 객체 지향 원리 적용

kiteB·2021년 8월 21일
0

Spring 강의노트

목록 보기
10/24
post-thumbnail

📌 김영한 선생님의 스프링 핵심 원리 - 기본편 강의를 들으면서 공부한 내용을 정리한 게시물입니다.

예제를 마저 따라해보자.


[ 새로운 할인 정책 개발 ]

지난 시간에 구현했던 주문과 할인 정책을 다시 떠올려보자!

할인 정책은 모든 VIP는 1,000원을 할인해주는 고정 금액 할인을 적용해달라. (나중에 변경될 수 있다.)

지난 시간에 고정 금액 할인을 적용하기 위해서 FixDiscountPolicy을 구현했다.

하지만 갑자기 기획자가 고정 금액 할인이 아닌 정률 금액 할인으로 변경해달라고 한다면..?

👩🏻‍💻 개발자: 안 들린다~~~

라~고~ 하지 말고 우리의 코드를 다시 한 번 살펴보자!

사실 우리는 지금까지 변화에 유연하게 대응할 수 있게끔 객체 지향 설계 원칙을 준수하여 개발을 해왔다.

우리 코드가 객체 지향 설계 원칙을 준수했는지 확인도 해보고 기획자가 원하는 정률 금액 할인으로 코드도 수정해보자!

설계

RateDiscountPolicy에 대한 코드를 작성하고 테스트까지 해보자!

🔗 코드 확인하기


[ 새로운 할인 정책 적용과 문제점 ]

기존 정책인 FixDiscountPolicy 대신, 위에서 추가한 RateDiscountPolicy를 적용해보자!

RateDiscountPolicy를 적용하려면 OrderServiceImpl 코드를 FixDiscountPolicy에서 RateDiscountPolicy직접 고쳐주자!

🔗 코드 확인하기

오잉,,, 이상한 점을 못 느끼셨나요,,?

우리는 SOLID 원칙을 지키면서 개발을 하려고 했는데 RateDiscountPolicy를 적용하려면 클라이언트인 OrderServiceImpl를 직접 수정해야 하기 때문에 SOLID를 위반해버렸다! 😥

문제점을 제대로 정리해보자면 다음과 같다.

📌 문제점 발견

  • 역할과 구현을 충실하게 분리했다. → ⭕
  • 다형성을 활용하고, 인터페이스와 구현 객체를 분리헀다. → ⭕
  • 객체 지향 설계 원칙(OCP, DIP)을 충실하게 구현했다. → ❌
    • DIP: 인터페이스(FixDiscountPolicy, RateDiscountPolicy) 뿐만 아니라 구현 클래스(DiscountPolicy)에도 의존하고 있다.
    • OCP: 현재 코드는 기능을 확장해서 변경하면, 클라이언트 코드에 영향을 주기 때문에 위반!

왜 지금의 코드는 SOLID를 위반할 수 밖에 없을까?

기대했던 의존관계

👩🏻‍💻 개발자: 이제 OrderServiceImplDiscountPolicy만 의존하게 되겠지? 😀

실제 의존관계

💻 OrderServiceImpl: 헤헤 사실 아니지롱! 나는 FixDiscountPolicy에도 의존해~😽

OrderServiceImplFixDiscountPolicy에 의존하고 있기 때문에 정책을RateDiscountPolicy로 바꾸려면 어쩔 수 없이 OrderServiceImpl의 코드를 바꿀 수 밖에 없다😥

👩🏻‍💻 개발자: 이러면 DIPOCP를 위반해버리잖아..😞

정책 변경

👩🏻‍💻 개발자: 안되겠다 OrderServiceImplFixDiscountPolicyRateDiscountPolicy에 의존하지 않게 코드를 바꿔버리자!

인터페이스에만 의존하도록 바꾸자!

문제가 되는 코드는 바로 여기였다.

public class OrderServiceImpl implements OrderService {
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
 private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}

👩🏻‍💻 개발자: 인터페이스에만 의존하게 하려면 이렇게 하면 되겠지?

public class OrderServiceImpl implements OrderService {
 private DiscountPolicy discountPolicy;
}

✋🏻 멈춰!!!
이렇게 하면 무시무시한 NPE(널포인트 에러)가 발생합니다😥 (구현체가 없기 때문 ㅠㅠ)

이렇게 하지 말고 OrderServiceImpl가 아닌 다른 객체에서 할인 정책의 구현 객체를 대신 생성해주고 주입해주자!



[ 관심사의 분리 ]

지금까지 상황을 정리해보자.
우리는 OCP, DIP 원칙을 지키기 위해서 노력해왔지만 여전히 해결하지 못하고 있다.

OrderServiceImpl가 할인 정책이 바뀔 때마다 직접!FixDiscountPolicyRateDiscountPolicy의 구현체들의 생성자들까지 호출해왔기 때문이다.

💻 OrderServiceImpl : 살려줘..

이렇게 OrderServiceImpl에게 과도하게 일을 시키지 말고 다른 친구를 데려오자😢

AppConfig

🙇🏻‍♀️ AppConfig: 안녕하세요 새로 온 일꾼입니다!

위에서 말했던 "OrderServiceImpl가 아닌 다른 객체에서 할인 정책의 구현 객체를 대신 생성해주고 주입하는 역할"을 해줄 친구가 바로 AppConfig이다.

📌 AppConfig

  • 애플리케이션의 실제 동작에 필요한 구현 객체를 생성한다.
  • 생성한 객체 인스턴스의 참조를 생성자를 통해서 주입(연결)한다.

🔗 코드 확인하기

AppConfig 친구를 통해서 이제 모든 구현체는 다른 구현체에 의존하지 않게 되었다!

클래스 다이어그램

  • 객체의 생성과 연결은 AppConfig가 담당한다.
  • DIP 완성: MemberServiceImplMemberRepository인 추상에만 의존하면 된다. 이제 구체 클래스를 몰라도 된다.
  • 관심사의 분리: 객체를 생성하고 연결하는 역할과 실행하는 역할이 명확히 분리되었다.

회원 객체 인스턴스 다이어그램

  • appConfig 객체는 memoryMemberRepository 객체를 생성하고 그 참조값을 memberServiceImpl을 생성하면서 생성자로 전달한다.
  • 클라이언트인 memberServiceImpl 입장에서 보면 마치 외부에서 주입해주는 것 같다고 해서 DI(의존관계 주입 또는 의존성 주입)라고 한다.

💡 정리

  • AppConfig를 이용하여 관심사를 확실하게 분리했다.
  • AppConfig구체 클래스를 선택한다. 애플리케이션이 어떻게 동작해야 할지 전체 구성을 책임진다.

[ AppConfig 리팩터링 ]

현재 AppConfig를 보면 중복도 있고, 역할에 따른 구현이 잘 안보인다😥

기대하는 그림

🔗 코드 확인하기

  • new MemoryMemberRepository() 부분의 중복이 제거되면서, MemoryMemberyRepository를 다른 구현체로 변경할 때 손쉽게 바꿀 수 있다!
  • AppConfig를 보면 역할과 구현 클래스가 한눈에 보여서 애플리케이션의 전체 구성을 빠르게 파악할 수 있다!

[ 📘 오늘의 TIL 정리 ]

드!디!어! 정말로 SOLID 원칙에 위배되지 않는, 객체 지향 설계 원칙을 준수하며 개발을 하였다.

만세!

profile
🚧 https://coji.tistory.com/ 🏠

0개의 댓글