DI(의존관계 주입)
코드를 짜면서 내부 로직을 변경해야 하는 경우에 OCP 원칙이 잘 지켜지지 않을 수 있다. OCP 원칙과 DIP 원칙을 준수하기 위해서 DI를 진행한다.
참고)
OCP: 기존 코드를 변경하지 않는 것
DIP: 클래스가 아닌 인터페이스에 의존하는 것
package hello.core.order;
import hello.core.discount.DiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
public class OrderServiceImpl implements OrderService{
//인터페이스에만 의존 (DIP 원칙 준수)
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);
}
}
위 코드의 OrderServiceImpl은 인터페이스 OrderService를 구현한 클래스이다.
또한 내부 변수로는 MemberRepository memberRepository와 DiscountPolicy discountPolicy가 있다. 이 둘은 모두 인터페이스 변수이다. 클래스 참조 변수가 아닌 인터페이스 참조변수를 사용한 이유는 DIP 원칙을 준수하기 위함이다.
OrderServiceImpl에 사용된 인터페이스
package hello.core.member;
public interface MemberRepository {
void save(Member member);
Member findById(Long memberId);
}
package hello.core.discount;
import hello.core.member.Member;
public interface DiscountPolicy {
// return이 할인 대상 금액
int discount(Member member,int itemPrice);
}
의존관계를 보면 OrderServiceImpl은 인터페이스에만 의존하고 있다. 즉 DIP 원칙이 준수되고 있다는 것이다.
그러나 지금 OrderServiceImpl의 생성자를 보면 외부로부터 생성자로 인자 값이 들어와야 한다는 사실을 확인 할 수 있다.
이러한 문제를 DI를 통해서 해결한다. 사실 별게 아니고 그냥 인자값 주입을 담당하는 클래스를 하나 만들면 된다.
의존관계 주입 담당 클래스 AppConfig
package hello.core;
import hello.core.discount.FixDiscountPolicy;
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;
/**
* Dip 원칙의 준수를 위해서 따로 생성한 AppConfig 클래스
*
* 생성자 주입을 통해서 service 클래스(OrderServiceImpll)은
* 의존관계에 대한 고민을 더 이상 하지 않아도 되게 되었다.
*
* 더 이상 의존 관계 변경시에 클라이언트 코드를 변경하지 않아도 되고
* AppConfig 내부 메소드의 인자만 변경해주면 되게 되었다.
*/
public class AppConfig {
/**
* AppConfig는 어플리케이션의 실제 동작에 필요한 구현 객체의 생성을 담당한다.
* 생성한 객체의 인스턴스의 참조를 "생성자 주입"해준다.
*/
public OrderService orderService(){
//생성자 주입
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
}
주석을 잘 읽어보면 생성자 주입이라는 말이 자주 나오는데, 이는 그냥 대상 클래스(여기서는 OrderServiceImpl)의 생성자로 새로운 객체를 생성해서 그 참조를 인자로 넣어주는 것이다.
여기서는 MemoryMemberRepository와 FixDiscountPolicy 인스턴스를 생성해서 참조값을 넣어주고 있는데 이 둘은 각각 OrderServiceImpl에 있는 멤버
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
의 하위 클래스이다.
MemberRepository의 하위클래스 MemoryMemberRepository
package hello.core.member;
import java.util.HashMap;
import java.util.Map;
public class MemoryMemberRepository implements MemberRepository{
private static Map<Long, Member> store = new HashMap<>();
@Override
public void save(Member member) {
store.put(member.getId(), member);
}
@Override
public Member findById(Long memberId) {
return store.get(memberId);
}
}
DiscountPolicy의 하위클래스 FixDiscountPolicy
package hello.core.discount;
import hello.core.member.Grade;
import hello.core.member.Member;
public class FixDiscountPolicy implements DiscountPolicy{
private final int discountFixAmount = 1000;
@Override
public int discount(Member member,int itemPrice) {
if(member.getGrade() == Grade.VIP)
return discountFixAmount;
else
return 0;
}
}
DI의 결과
현재 OrderServiceImpl은 어떠한 객체가 내부로 주입될지 알 수 없다.
AppConfig에서 생성자 주입을 담당한 결과, 어떠한 객체를 주입할 지는 오직 AppConfig에서 결정하게 되었다.
결과론적으로 OrderServiceImpl은 의존관계는 신경 쓸 필요 없이 오직 실행에만 집중하면 된다.
'Spring' 카테고리의 다른 글
Spring- 스프링 컨테이너로 등록하기(1) (0) | 2023.01.17 |
---|---|
Spring- IoC, DI 컨테이너 (0) | 2023.01.17 |
Spring- 객체 지향의 SOLID 원칙 (0) | 2023.01.12 |
Spring- 자바 코드로 직접 스프링 빈 등록 (0) | 2023.01.04 |
Spring- 컴포넌트 스캔과 자동 의존관계 설정 (0) | 2023.01.04 |