[Spring] 3-2. 관심사의 분리

송광호·2023년 12월 8일

[Spring]

목록 보기
12/41
post-thumbnail

Spring 시리즈는 혼자 공부하며 기록으로 남기고, 만약 잘못 학습 한 지식이 있다면 공유하며 피드백을 받고자 작성합니다.
스프링에 대해 깊게 공부해보고자 인프런의 김영한 강사님께서 강의를 진행하시는 (스프링 핵심 원리 - 기본편) 강의를 수강하며 정리하는 글입니다.
혹여나 글을 읽으시며 잘못 설명된 부분이 있다면 지적 부탁드리겠습니다.


관심사의 분리

  • 애플리케이션을 하나의 공연으로 생각해보자. 그리고 인터페이스들을 배역(배우 역할)이라고 생각하자.
  • 그런데 배역에 맞는 배우를 선택하는것은 누구인가?
  • 로미오와 줄리엣 공연을 하면 각 배역을 누가할지는 배우들이 정하는것이 아니다. 감독이 정해야한다.
  • 지금까지의 코드는 마치 로미오 역할을 하는 배우가 줄리엣 역할을 하는 배우(구현체)를 직접 초빙하는것과 유사하다.

관심사를 분리하자

  • 배우는 본인의 역할인 배역을 수행하는 것에만 집중한다.
  • 배우는 상대가 어떤 배우가 선택되더라도 똑같이 공연을 할 수 있어야한다.
  • 자 그럼 역할에 맞는 배우를 지정할 공연 기획자가 필요하니 만들어보자.

AppConfig!

  • 구현 객체를 생성하고, 연결하는 책임을 가지는 별도의 공연기획자의 등장이다.
package hello.core;

import hello.core.discount.FixDiscountPolicy;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

public class AppConfig {

    public MemberService memberService() {
        return new MemberServiceImpl(new MemoryMemberRepository());
    }

    public OrderService orderService() {
        return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
    }
}
  • AppConfig는 애플리케이션의 실제 동작에 필요한 구현 객체를 생성한다.
    • MemberServiceImpl
    • MemoryMemberRepository
    • OrderServiceImpl
    • FixDiscountPolicy
  • Appconfig는 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해 주입해준다.

MemberServiceImpl 생성자 주입

public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository;

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}
  • 이제 더이상 MemberServiceImpleMemberRepository의 구현체를 의존하지 않는다.
  • 단지 MemberRepository 인터페이스에만 의존한다.
  • 생성자를 통해 어떤 구현체가 들어올지는 알 수 없다. JdbcMemberRepository가 올수도 있고,
    MemoryMemberRepository가 올 수도 있다.

OrderServiceImpl 생성자 주입

package hello.core.order;

import hello.core.discount.DiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;

public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}
  • 설계 변경으로 OrderServiceImple이 더이상 MemoryMemberRepositoryFixDiscountPolicy를 의존하지 않는다.
  • 인터페이스만 의존하게 구조가 변경되었다.

클래스 다이어그램

  • 객체의 생성과 연결은 Appconfig가 담당한다
  • 드디어 DIP가 만족한다. MemberServiceImplMemoryMemberRepository를 의존하지 않는다.

인스턴스 다이어그램

  • appConfig 객체는 memoryMemberRepository를 생성하고, 생성된 참조값을 memberServiceImpl을 생성하면서 생성자로 전달한다.
  • 클라이언트입장에서 의존관계를 마치 외부에서 주입하는 것처럼 보여 DI(Dependency Injection) 우리말로 의존관계 주입 또는 의존성 주입이라 한다.

실행(OrderApp)

package hello.core;

import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.order.Order;
import hello.core.order.OrderService;

public class OrderApp {
    public static void main(String[] args) {

        AppConfig appConfig = new AppConfig();
        OrderService orderService = appConfig.orderService();
        MemberService memberService = appConfig.memberService();
    }
}

정리

  • AppConfig를 통해 관심사를 확실하게 분리한다.
  • AppConfig는 공연 기획자 라고 생각하자.

AppConfig 리팩터링

  • 위의 AppConfig 코드를 보면 중복이 있고, 역할구현이 잘 보이지 않는다.

AppConfig 리팩터링 후

package hello.core;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

public class AppConfig {

    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    public DiscountPolicy discountPolicy() {
        return new FixDiscountPolicy();
    }

    private MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }   
}
  • AppConfig를 보면 역할과 구현이 한눈에 들어온다.
  • 만약 JdbcMemberRepository로 구현체를 변경하고싶으면 마지막 메서드의 MemoryMemberRepositoryJdbcMemberRepository로 변경만 하면 끝난다.

0개의 댓글