본문 바로가기
프로그래밍/Spring

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

by YuminK 2023. 7. 14.

새로운 할인 정책이 추가된다면, Service내의 Policy를 다시 할당해야 한다. 

 

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

private final DiscountPolicy discountPolicy = new RateDiscountPolicy();

 

다형성을 활용하고 인터페이스와 구현 객체를 분리했다. 하지만 OCP, DIP원칙을 지키고 있지 않다.

클라이언트 코드 자체를 수정했으므로 OCP 원칙에 어긋난다. 인터페이스를 참조하지만 구현 클래스에도 의존하고 있으므로(RateDiscountPolicy) DIP 원칙에 어긋난다. 

 

이러한 문제를 해결하기 위해 AppConfig클래스를 만들어 원하는 할인 정책 및 저장소 클래스를 할당한다. 

기존에 서비스에서는 선언과 동시에 할당을 받지 않고 생성자를 통해 할당 받도록 한다. 

 

public class OrderServiceImpl implements OrderService {

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

private DiscountPolicy discountPolicy;

}

 

클라이언트가 할당되는 클래스를 지정한다는 것은, 배우가 원하는 배역을 선택하는 꼴이다. 기획자는 배우를 선택하는 입장이고 배우는 선택을 받아야 한다.

package hello.core;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
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;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

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

    @Bean
    public static MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

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

    @Bean
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }

}

 

제어의 역전 IOC(Inversion Of Control)

구현 객체가 프로그램의 제어 흐름을 스스로 조종하는 형태가 아니라, 외부에서 제어 흐름을 제어하는 것을 말한다. 

AppConfig에서 원하는 객체를 할당하여 제어 흐름을 가져가지만, 내부 클라이언트 코드(ex: OrderServiceImpl)은 어떠한 객체가 실행될지 알지 못한다. 단지 인터페이스의 함수를 호출시킨다. 

 

프레임워크 vs 라이브러리

내가 작성한 코드를 제어하고, 대신 실행하면 프레임워크이다. (JUnit)

내가 작성한 코드가 직접 제어 흐름을 담당한다면 라이브러리이다. 

 

의존관계 주입 DI(Dependency Injection)

OrderServiceImpl 은 DiscountPolicy 인터페이스에 의존한다. 실제 어떤 구현 객체가 사용될지는 모른다. 의존관계는 정적인 클래스 의존 관계(import된 클래스들)와, 실행 시점에 결정되는 동적인 객체(인스턴스) 의존 관계 둘을 분리해서 생각해야 한다.

 

런타임에 외부에서 실체 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결되는 것을 뜻한다. DI를 사용하면 클라이언트 코드의 변경없이 동작되는 대상의 타입 인스턴스를 변경할 수 있다. 

 

AppConfig처럼 의존 관계를 연결해주는 것을 IOC 컨테이너, DI 컨테이너라고 부른다. 

최근에는 DI 컨테이너라고 하는 편.

댓글