반응형

관심사의 분리

  • 현재 주문에서는 할인 정책에 대한 것도 신경 쓰고 있다.
    • 주문에 대한 책임 뿐만이 아니라 할인 정책을 정해야하는 책임도 가지고 있다.
  • 주문은 주문과 관련된 역할을 수행하는 것에 집중해야하고 어떤 할인 정책이 적용되더라도 똑같이 주문할 수 있어야 한다.
  • 할인 정책을 정하는 책임을 담당하는 별도의 책임자가 필요하다.

 

AppConfig 등장

  • 애플리케이션의 전체 동작 방식을 구성(config)하기 위해, 구현 객체를 생성하고, 연결하는 책임을 가지는 클래스
    public class AppConfig {
        //생성자 주입
        public MemberService memberService() {
            return new MemberServiceImpl(new MemoryMemberRepository());
        }
        //생성자 주입
        public OrderService orderService() {
            return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
        }
    }
    • AppConfig는 애플리케이션의 실제 동작에 필요한 구현 객체를 생성한다.
    • AppConfig는 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해서 주입(연결)해준다.
  • MemberServiceImpl - 생성자 주입
    public class MemberServiceImpl implements MemberService {
    
        private final 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);
        }
    }
    • 관심사의 분리로 DIP도 만족하게 되었다.
    • MemberServiceImpl 의 생성자를 통해서 어떤 구현 객체를 주입할지는 오직 외부( AppConfig )에서 결정된다.
    • 의존관계에 대한 고민은 외부( AppConfig )에 맡기고 실행에만 집중하면 된다.
  • 클래스 다이어그램
    • 객체의 생성과 연결은 AppConfig 가 담당한다.
    • DIP 완성: MemberServiceImpl 은 MemberRepository 인 추상에만 의존하면 된다. 이제 구체 클래스를 몰라도 된다.
    • 관심사의 분리: 객체를 생성하고 연결하는 역할과 실행하는 역할이 명확히 분리되었다.

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

 

  • OrderServiceImpl - 생성자 주입
    public class OrderServiceImpl implements OrderService {
    //  private final MemberRepository memberRepository = new MemoryMemberRepository();
        private final MemberRepository memberRepository; // 구현체 의존 제거
    //  private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
    //  private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
        private final DiscountPolicy discountPolicy; // 구현체 의존 제거
    
        public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
            this.memberRepository = memberRepository;
            this.discountPolicy = discountPolicy;
        }
    
        @Override
        public Order createOrder(Long memberId, String itemName, int itemPrice) {
            Member member = memberRepository.findById(memberId);
            int discountPrice = discountPolicy.discount(member, itemPrice);
    
            return new Order(memberId, itemName, itemPrice, discountPrice);
        }
    }
    • 관심사의 분리로 DIP도 만족하게 되었다.
    • OrderServiceImpl 입장에서 생성자를 통해 어떤 구현 객체가 들어올지(주입될지)는 알 수 없다.
    • OrderServiceImpl 의 생성자를 통해서 어떤 구현 객체을 주입할지는 오직 외부( AppConfig )에서 결정한다.
      • OrderServiceImpl 에는 MemoryMemberRepository , FixDiscountPolicy 객체의 의존관계가 주입된다.
    • 의존관계에 대한 고민은 외부( AppConfig )에 맡기고 실행에만 집중하면 된다.

 

AppConfig 활용

  • MemberApp
public class MemberApp {

    public static void main(String[] args) {
        AppConfig appConfig = new AppConfig();
        MemberService memberService = appConfig.memberService();
//        MemberService memberService = new MemberServiceImpl();

        Member member = new Member(1L, "memberA", Grade.VIP);
        memberService.join(member);

        Member findMember = memberService.findMember(1L);
        System.out.println("new member = " + member.getName());
        System.out.println("find member = " + findMember.getName());

    }
}

 

  • OrderApp
public class OrderApp {

    public static void main(String[] args) {
        AppConfig appConfig = new AppConfig();
        MemberService memberService = appConfig.memberService();
        OrderService orderService = appConfig.orderService();
//        MemberService memberService = new MemberServiceImpl();
//        OrderService orderService = new OrderServiceImpl();

        Long memberId = 1L;
        Member member = new Member(memberId, "memberA", Grade.VIP);
        memberService.join(member);

        Order order = orderService.createOrder(memberId, "itemA", 10000);

        System.out.println("order = " + order);
        System.out.println("order.calculatePrice = " + order.calculatePrice());
    }
}

 

  • 각 서비스 구현체 내부적으로 존재하는 의존 객체들이 초기에 주입됨으로써 DIP를 만족할 수 있게 되었다.

[참고자료]

스프링 핵심 원리 - 기본편, 김영한

반응형

+ Recent posts