스프링 핵심원리 기본편
Intro
김영한님의 Spring 핵심원리 기본편을 듣고 정리한 내용을 공유합니다.
요구사항
- 회원 등급에 따라 VIP라면 고정 금액 할인
- 회원 등급에 따라 VIP라면 정률 할인
- 고객이 오픈직전에 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) 의존성 주입이라고 한다.
Leave a comment