[기본기] 5-4. 관심사의 분리

khyojun·2022년 9월 15일
1
post-thumbnail

본 게시글은 김영한님의 스프링 핵심 원리 기본편을 정리한 글입니다.


🔍 이전 시간 Review

이전 글에서 마지막에 이제 OCP, DIP를 지키기 위하여서 구현체를 의존하지 않게 하기 위하여서

// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy;

위의 주석 코드를 아래처럼 바꿔서 진행을 해야 한다고 하였다. 그러나 이렇게 진행할 경우 구현체가 없이 코드를 실행할 수가 없어 NPE가 발생하는 문제가 있다고 하였다. 그렇다면 이번 시간에는 이러한 문제를 해결할 수 있도록 하기 위하여서 어떻게 해야하는지 알아보자.

📌 잠시... 관심사를 분리시켜본다.

지금 우리가 생각해야될 것이 구현체에 신경을 쓰지 않아도 추상체만 보고 할인 정책을 바꿀 수 있도록 변경을 해야한다. 이것을 기억하고 이야기를 들어보자!

✨ 다시 공연 현장으로

애플리케이션을 하나의 공연으로 생각을 할 때, 각각의 인터페이스를 배역(역할)이라고 생각을 하고, 배우를(구현)이라고 생각을 해본다.
그렇게 할 때 이 배역의 배우는 누가 하는 것일까?
현실세계에서는 이 역할을 기획자라는 사람이 나타나서 각 배역에 맞는 배우를 지정을 하게 된다. 배우가 지정하는 것이 아니란 이 말이다.
예를 들자면 디카프리오라는 배우가 로미오 역할을 하는데 줄리엣을 연기해야 하는 여자 주인공을 직접 초빙해야 하는 것과 같은 상황이 우리의 코드 상황이었다. 그래서 디카프리오는 엄청난 열정맨이기에 여러가지 책임을 도맡고 있다는 상황이 된 것이었다.

✨ 각자의 역할에만 신경을 쓰자

  • 배우는 결론적으로 자기가 맡은 배역에만 최대한 신경을 써야 한다.
  • 로미오를 맡은 배우는 줄리엣에 어떤 배우가 오던 똑같이 공연을 할 수 있어야 한다.
  • 그렇다면 이제는 배우가 섭외까지 도맡는 것이 아닌 이런 외부적인 역할을 다 할 수 있는 기획자라는 사람이 나타날 때가 되었다.
  • 이제 기획자와 배우의 책임을 확실히 분리를 해보자.

📌 다시 코드로...

❗ AppConfig 기획자의 등장

애플리케이션의 전체 동작을 기획,구성(config)하기 위한 구현 객체를 생성하고, 연결을 하는 책임을 가지는 별도의 config 클래스를 하나 만들어보자.

📂 AppConfig

public class AppConfig {

    public MemberService memberService(){
        return new MemberServiceImpl(new MemoryMemberRepository());
    }
    public OrderService orderService(){
        return new OrderServiceImpl(new MemoryMemberRepository(),  new FixDiscountPolicy());
    }
}

위 코드를 보게 되면 Config 클래스에서 memberService, orderService를 부르게 되면 실질적으로 각 구현 객체를 생성을 하게 된다. MemberServiceImpl, MemoryMemberRepository, OrderServiceImpl, FixDisCountPolicy

AppConfig는 생성한 객체 인스턴스의 참조(래퍼런스)를 생성자를 통해서 주입(연결)해준다.
MemberService -> MemoryMemberReposiotry
OrderServiceImpl -> MemoryMemberRepository, FixDiscountPolicy

그렇다면 AppConfig에서 만든 내용을 바탕으로 기존에 있는 각 구현객체 코드들에도 생성자를 주입시켜주자.

📂 MemberServiceImpl

public class MemberServiceImpl implements MemberService{


    MemberRepository memberRepository;
	//추가된 내용
    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Override
    public void join(Member member) {
        memberRepository.save(member);
    }

    @Override
    public Member findMember(Long memberId) {
        return memberRepository.findById(memberId);
    }
}

이제 위의 코드에서 생성자를 통하여서 memberRepository를 받아오는데 구현객체가 어떤 것이 오는지는 MemberServiceImpl에서는 알 수 없다. 이제는 AppConfig를 통하여서 어떤 Repository를 사용을 할 지 결정이 되어지기 때문에 결론적으로는 이제는 구현체에 신경을 쓰지 않고 그저 추상화에만 신경을 써주면 된다. -> 의존관계에 대한 고민은 외부에 맡기고 실행에만 집중을 하면 된다.

위와 같이 변경이 되었을때 이제 클래스 다이어그램과 인스턴스 다이어그램은 어떻게 되었는지 확인을 해보자.

📂 OrderServiceImpl

public class OrderServiceImpl implements OrderService{
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;  
	//추가된 코드
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) { // impl 입장에서는 외부에서 의존성 주입 DI
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);

        int discountP=discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountP);
    }
}

OrderServiceImpl도 마찬가지이다. 이제는 더 이상 구현 객체에 신경을 쓰지 않고 실행에만 집중을 하면 되는 코드로 바뀌게 되었다.

📌 마무리 하기 전 테스트를 꼭

이제 다 설정도 마무리 했으니 마지막으로 잘 되니 테스트코드를 꼭 작성해보도록 하자.

📂 MemberTest

public class memberTest {
    MemberRepository memberRepository;
    MemberService memberService;

    @BeforeEach
    void beforeEach(){
        AppConfig appConfig = new AppConfig();
        memberService = appConfig.memberService();
    }
    .
    .
    .
   

📂 OrderServiceTest

public class OrderServiceTest {


    MemberService memberService;
    OrderService orderService;
    @BeforeEach
    void beforeEach(){
         AppConfig appconfig = new AppConfig();
         memberService = appconfig.memberService();
         orderService = appconfig.orderService();
    }

.
.
.
.

그럼 다 끝났나?

설정상으로는 이제 문제는 없지만 더 깔끔하게 보이도록 다음시간에는 리팩토링을 해보도록 하자!

출처

  1. 김영한님의 스프링 핵심 원리 기본편(https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%95%B5%EC%8B%AC-%EC%9B%90%EB%A6%AC-%EA%B8%B0%EB%B3%B8%ED%8E%B8)
profile
코드를 씹고 뜯고 맛보고 즐기는 것을 지향하는 개발자가 되고 싶습니다

0개의 댓글