BACK/SPRING

[Spring] 스프링 핵심 원리 - 객체 지향 원리 적용(+ DI, IoC, 스프링 컨테이너)

연듀 2024. 1. 30. 17:39

 

 

memberRepository 인터페이스를 두 repository가 구현한다. 

memberService는 memberRepository 인터페이스만 의존하도록 한다.

 

package com.hello.core.member;

public class MemberServiceImpl implements MemberService{
    //private final MemberRepository memberRepository = new MemoryMemberRepository(); // 실제 할당하는 부분이 구현체를 의존
    // 인터페이스, 구현체 모두 의존 => dip 위반
    private final MemberRepository memberRepository; // 추상화에만 의존하도록 한다. 

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }

    @Override
    public void join(Member member) {
        memberRepository.save(member); // MemoryMemberRepository의 save 호출
    }

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

 

 

 

orderService는 MemberRepository, DiscountPolity 인터페이스를 의존한다.

 

public class OrderServiceImpl implements OrderService{
    //private final MemberRepository memberRepository = new MemoryMemberRepository();
    //private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
    //private final DiscountPolicy discountPolicy = new RateDiscountPolicy(); 
    // 위의 방식들은 추상 뿐만 아니라 구체 클래스도 의존 - dip 위반
    // fix -> Rate 로 변경하는 순간 현재 OrderServiceImpl(클라이언트)의 소스코드도 함께 변경해야 한다 -> OCP(변경하지 않고 확장) 위반


    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
    // final 이면 반드시 생성자를 통해 할당돼야 함
    // dip, ocp 만족
    

    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);
    }
}

 

 

누군가가 클라이언트인 OrderServiceImpl 에 DiscountPolicy 의 구현 객체를 대신 생성하고 주입해주어야 한다.

 

애플리케이션의 전체 동작 방식을 구성(config)하기 위해, 구현 객체를 생성하고, 연결하는 책임을 가지는 AppConfig라는 별도의

설정 클래스를 만들자.

 

어떤 구현 객체를 주입할지는 오직 외부(AppConfig)에서 결정된다.

 

 

 

public class AppConfig { 
	// 애플리케이션의 실제 동작에 필요한 구현 객체를 생성
    // 객체의 생성과 연결을 담당
	// DI - 의존성 주입
    public MemberService memberService(){
        return new MemberServiceImpl(memberRepository()); // 객체를 생성하고 참조값을 memberServiceImpl을 생성할 때 생성자로 전달
    }

    private static MemoryMemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

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

    public DiscountPolicy discountPolicy(){
       // return new FixDiscountPolicy();
        return new RateDiscountPolicy(); // 유동적으로 설정
    }
}

 

 

제어의 역전 IoC

(Inversion of Control)

프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것

  • 프레임워크: 프레임워크가 코드를 제어하고 대신 실행
  • 라이브러리: 내가 작성한 코드가 직접 제어의 흐름을 담당 ex) java 객체 → xml, json 변환 라이브러리를 직접 호출

 

DI

(Dependency Injection)

 

의존관계 주입: 애플리케이션 실행 시점(런타임)에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달

클라이언트 코드를 변경하지 않고 ( = 정적인 클래스 의존관계 변경 X)

클라이언트가 호출하는 대상의 타입 인스턴스 변경 가능 ( = 동적인 객체 인스턴스의 의존관계 변경 가능)

 

 

 

IoC 컨테이너, DI 컨테이너

객체를 생성하고 관리하며 의존관계를 연결해 주는 것 (=AppConfig)

 

 

스프링 컨테이너

= ApplicationContext

ApplicationContext applicationContext = new
 AnnotationConfigApplicationContext(AppConfig.class);
  • @Configuration 이 붙은 파일을 설정 정보로 사용
  • 파일에 @Bean 이 적힌 메서드(=빈의 이름)를 모두 호출해 반환된 객체를 스프링 컨테이너에 스프링 빈으로 등록
  • applicationContext.getBean(메서드 이름, 타입)으로 빈을 찾음

 

 

 

 

참고) 스프링 핵심 원리 기본편

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#