[본 포스팅은 인프런 스프링 핵심 원리 - 기본 편을 기반으로 작성하였습니다.]
객체 지향 프로그래밍을 할 때에는 SOLID를 잘 지키면서 코딩해야 좋은 프로그램이 된다.
하지만 기존 코드를 보면
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
}
이러한 식으로 코드를 짜게 되었다.
코드를 보면
private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
이렇게 인터페이스(DiscountPolicy)도 참조하고 구현체(FixDiscountPolicy)도 참조하는 것을 볼 수 있다.
ex) A라는 공연에서 배우를 뽑을 때 감독이 뽑는 것이 아닌 남자배우가 여자배우를 뽑는 것과 같다.
※DIP 위반
이러한 코드의 문제점은 만약 고정할인(FixDiscountPolicy)에서 정률할인(RateDiscountPolicy)으로 변경하기 위해선
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
단순 이렇게 코드를 변경할 것이고 컴파일을 하면 문제가 없을 것이다.
하지만 이것은 OCP 위반이다
따라서 앞서 말한 SOLID 규칙에 어긋나게 된다.
AppConfig
DIP, OCP를 위반하지 않기 위해 만드는 것이 AppConfig다.
또한 애플리케이션의 전체 구성을 책임지는 담당자!
AppConfig를 통해 생성자 주입을 하게 된다.
AppConfig.java
public class AppConfig {
public MemberService memberService(){
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService(){
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
}
이렇게 AppConfig에서 환경을 설정해주고 강의 초반에 말해주셨던 것처럼 블록식으로 끼워 사용한다는 것이 이 얘기인 것 같다.
현재는 MemoryMemberRepository를 사용하여 내부 메모리를 사용하지만, 나중에는 DB로 교체될 경우 3번째 줄에 DB로 바꿔주기만 하면 된다.
AppConfig를 짜두었으면 이제 Impl 클래스를 수정해주면 된다. 수정은 아래처럼 해주면 된다.
(기존에는 Impl 내부에서 MemoryMemberRepository를 참조했음)
OrderServiceImpl.java
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);
}
}
변경된 MemberServiceImpl에서만 봤을 땐 현재 메모리를 사용하는지 DB를 사용하는지 모르고, 추상적으로 MemberRepository만 의존하고 있다.
※ DIP 준수!
그리고 AppConfig에서 수정(Memory -> DB)해주기 위해선 생성자를 만들어 생성자 주입을 시켜준다.
정리하자면 AppConfig는 환경을 설정(어떤 구현 객체를 주입할지)해준다고 보면 되고 Impl 클래스들은 추상적으로 참조만 하고 의존관계에 대한 고민은 외부에 맡기고 실행에만 집중하면 된다.
응용
OrderApp.java
public class OrderApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
OrderService orderService = appConfig.orderService();
//MemberService memberService = new MemberServiceImpl();
//OrderService orderService = new OrderServiceImpl();
Long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
Order order = orderService.createOrder(memberId, "itemA", 10000);
System.out.println("order = " + order);
}
}
OrderApp이라는 클래스에서 회원 정보를 넣고 이 회원이 itemA를 살 때 얼마의 할인을 받는지 보기 위한 코드이다.
먼저 AppConfig를 선언하였고 그 다음 선언한 MemberService(회원 정보)에게 appConfig.memberService()로 대입을 시켜주었고, OrderService도 똑같은 방법으로 대입시켜주었다.
그렇다면 OrderApp에서 대입을 위해 거치게 되는 순서는 아래처럼 된다.
OrderApp > AppConfig(MemoryMemberService) > MemberServiceImpl(생성자) |
'JAVA > Spring' 카테고리의 다른 글
[Spring] 롬복(Lombok) 설치 및 간단하게 사용 (feat. 최신 트렌드) (0) | 2022.09.02 |
---|---|
[Spring] 스프링 컨테이너가 싱글톤을 보장해주는 이유 (0) | 2022.09.01 |
[Spring] 스프링 컨테이너를 사용해야 하는 이유 (싱글톤 패턴의 문제점 해결) 그리고 주의점 (0) | 2022.09.01 |
[Spring] 기존 코드를 Spring으로 바꾸는 방법 (Configuration, Bean으로 찍먹) (0) | 2022.08.31 |
스프링 부트 프로젝트 쉽게 생성하기 start.spring.io / Spring initializr (0) | 2022.08.30 |