개발/🍃 Spring

🍀 [ 김영한 스프링 핵심 원리 - 기본편 ] Section04. 스프링 핵심 원리 이해2 - 객체 지향 원리 적용

정소은 2025. 1. 16. 23:52

 

 

 

[ 문제점 ]

클라이언트 코드(Service 구현체)에서 할인 정책의 구현체를 직접 생성 및 사용

DiscountPolicy discountPolicy = new FixDiscountPolicy();

=> OCP 위반 : 구현체의 변화가 필요할 때 클라이언트 코드를 수정해야 함

=> DIP 위반 : 인터페이스(역할)이 아닌 구현체를 참조

 

 

[ 해결 방안 ]

 

구성 영역사용 영역을 분리하자

구성 영역 : 클라이언트 코드에서 사용하기로 결정한 구현체들 정의

사용 영역 : 주입받은 구현체로 로직 수행 / 사용 영역에서는 각 정책에 대한 구현체는 드러나 있지 않음

 

구성 영역 - AppConfig

public class AppConfig {

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

    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

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

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

-  생성자 주입 : 현재 시점에서 사용하기로 결정한 구현체 객체를 생성자를 통해 서비스 구현체에 주입

-  역할과 구현 간의 구성이 잘 드러나도록 코드 작성

 

사용 영역 - MemberServiceImpl, OrderServiceImpl

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

 

2 )  OverServiceImpl

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

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

 

-  구현체가 아닌 역할만 의존 = ServiceImpl class가 아닌 Service interface를 참조 = 구현체 드러나지 않음

 

 

[ IoC와 DI ]

IoC ( 제어의 역전 )

: 프로그램의 제어 흐름을 외부에서 관리하도록 하는 것

 

클라이언트 코드(Service class)에서 사용할 구현체를 직접 생성하고 활용하는 것이 아니라

AppConfig에서 각 Service class에서 사용할 구현체를 정의하고

Service class는 자신의 로직을 실행하는 역할만 담당하도록 할 때 제어의 역전이 일어났다고 말한다

 

DI ( 의존관계 주입 )

정적 의존관계 : import문을 통해 명시되어 있는 의존관계

동적 의존관계 : 애플리케이션 실행 시점에 실제 생성된 객체 인스턴스의 참조가 연결된 의존관계

 

애플리케이션 실행 시점에 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실질적인 의존 관계가 연결되는 것을 의존관계 주입이라 한다

-  클라이언트 코드 변경 없이 클라이언트가 호출하는 대상의 타입 인스턴스(구현체) 변경 가능

-  정적인 클래스 의존관계 변경 없이 동적인 객체 인스턴스 의존관계 변경 가능

 

 

[ Spring으로 구현 ]

AppConfig - 구성 영역

@Configuration
public class AppConfig {

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

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

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

    @Bean
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
        //return new FixDiscountPolicy();
    }
}
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);

-  @Configuration, @Bean 통해서 Spring Container에 객체 저장하고 관리하도록 설정

 

 

 

왜 스프링 컨테이너에서 객체 관리하도록 하는 게 좋은가??

추후 강의에서 설명 예정...