스프링 핵심원리 기본편

진연석

Updated:

Intro

김영한님의 Spring 핵심원리 기본편을 듣고 정리한 내용을 공유합니다.

요구사항

  1. 회원 등급에 따라 VIP라면 고정 금액 할인
  2. 회원 등급에 따라 VIP라면 정률 할인
  3. 고객이 오픈직전에 1, 2번 중 하나를 선택

요구사항이 정해지지 않았을 때 유연한 설계가 가능하도록 객체지향 설계를 해야 한다.

할인 정책 인터페이스

public interface DiscountPolicy {
    /**
     * @return 할인 대상 금액
    */
    int discount(Member member, int price);
}

고정 금액 할인 구현체

public class FixDiscountPolicy implements  DiscountPolicy {

    private int discountFixAmount = 1000; // 1000원 할인

    @Override
    public int discount(Member member, int price) {
        if(member.getGrade() == Grade.VIP) {
            return discountFixAmount;
        } else {
            return 0;
        }
    }
}

정률 할인 구현체

public class RateDiscountPolicy implements  DiscountPolicy{

    private int discountPercent = 10;


    @Override
    public int discount(Member member, int price) {
        if(member.getGrade() == Grade.VIP) {
            return price * discountPercent / 100;
        } else {
            return 0;
        }
    }
}

주문 서비스 인터페이스

public interface OrderService {
    Order createOrder(Long memberId, String itemName, int itemPrice);
}

주문 서비스 구현체

public class OrderServiceImpl implements OrderService {
    private final MemberRepository memberRepository = new MemoryMemberRepository();
    // private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
    private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
    
    @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의 위배된다.
  • 기존 고정 금액 할인 구현체를 정률 할인 구현체로 교체해야 한다. -> OCP의 위배된다.

DIP (Dependency inversion principle) 의존관계 역전 원칙이란?

  • 프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다.
  • 구현 클래스에 의존하지 말고, 인터페이스에 의존하라
  • ex) 연극으로 비유하면 역할에 집중해야지 배우에게 집중하면 안된다.

OCP (Open/closed principle) 개방-폐쇄 원칙이란?

  • 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.

해결방법

  • DIP를 위반하지 않도록 인터페이스에만 의존하도록 변경
  • 클라이언트인 OrderServiceImpl에 DiscountPolicy의 구현 객체를 대신 생성하고 주입해야 한다.
public class OrderServiceImpl implements OrderService {
    // private final MemberRepository memberRepository = new MemoryMemberRepository();
    // private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
}

관심사의 분리

  • 애플리케이션의 전체 동작 방식을 구성하기 위해 구현 객체를 생성하고, 연결하는 설정 클래스 생성
public class AppConfig {
    public MemberService memberService() {
        return new MemberServiceImpl(new MemoryMemberRepository());
    }

    public OrderService orderService() {
        return new OrderServiceImpl(
        new MemoryMemberRepository(),
        new FixDiscountPolicy());
    }
}
  • 이처럼 AppConfig는 실제 동작에 필요한 구현 객체를 생성하고 생성자를 통해 구현체를 연결 해줌으로써 객체를 생성하고 연결하는 역할과 실행하는 역할이 명확이 분리되었다.
  • 클라이언트인 MemberServiceImpl 입장에서 보면 의존 관계를 외부에서 주입해주는 것 같다고 하여 DI(Dependency Injection) 의존성 주입이라고 한다.

Tags:

Categories:

Updated:

Leave a comment