[스프링 핵심원리 기본편] 3.(2) 관심사의 분리 ; Config

코린이서현이·2023년 11월 11일
0

👏들어가면서👏

저번에 다형성과 구현과 역할을 분리해서 코드를 짰지만, OCP원칙과 DIP원칙을 위반하는 것을 확인했다. 

📌 OCP : 개방-폐쇄 원칙 
- 변경 시, 기존 코드를 변경하지 않아도 가능 해야한다.
📌 DIP : 의존 관계 역전 원칙
- 추상화에 의존해야한다.

🤔 왜? 구현체에서도 의존하고 있었기 때문
→ private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
⚒️ private final DiscountPolicy discountPolicy;

이를 위한 실제 구현은 어떻게?

🎯 목표

📌 관심사를 분리하자
📌 config 
📌 할인 정책 변경하기

📌 관심사를 분리하자

🤔 현재상황

  • 구현체에서 다른 구현체를 직접 선언

⬇️ OrderServiceImpl 코드 내용

public class OrderServiceImpl implements hello.core.order.OrderService {
  private final hello.core.member.MemberRepository memberRepository;
  private final hello.core.discount.DiscountPolicy discountPolicy;

  public OrderServiceImpl() { /* compiled code */ }

  public hello.core.order.Order createOrder(java.lang.Long memberID, java.lang.String itemName, int itemPrice) { /* compiled code */ }
}

❣️ 관심사를 분리하자

  • 역할에 맡는 구현체를 담당하는 별도의 프로그램 기획자가 필요하다.

📌 config로 DIP 지키기

  • 애플리케이션의 전체 동작 방식을 구성(config) 하기 위해, 구현 객체를 생성하고 연결하는 별도의 설정 클래스를 만들자.

AppConfig 작성하기

📄 Appconfig

public class AppConfig {
 public MemberService memberService() {
 return new MemberServiceImpl(new MemoryMemberRepository());
 }
 public OrderService orderService() {
 return new OrderServiceImpl(
 new MemoryMemberRepository(),
 new FixDiscountPolicy());
 }
}

⚒️ AppConfig 리팩토리

  • 중복을 제거
  • 메소드를 통해 애플리케이션의 구성 파악을 쉽게한다.
public class AppConfig {

  public MemberService memberService() {
    return new MemberServiceImpl(memberRepsitory()); //참조값을 넣어준다.
  }

  private static MemberRepository memberRepsitory() {
    return new MemoryMemberRepository();
  }

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

  private static DiscountPolicy discountPolicy() {
    return new FixDiscountPolicy();
  }
}

⚒️ 다른 코드 수정

📄 MemberServiceImpl - 생성자 주입

public class MemberServiceImpl implements MemberService{
  private final MemberRepository memberRepository;

  public MemberServiceImpl(MemberRepository memberRepository) {
    this.memberRepository = memberRepository;
  }

👉 MemberServiceImpl은 이제부터 의존관계에 대한 고민은 외부에 맡기고 실행에만 집중하면 된다.

📄 OrderServiceImpl - 생성자 주입

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

다른 코드도 마저 수정

  • MemberAPP/ OrderApp / AppMemberServiceTest / OrderServiceTest 에서 사용하는 구현객체를 appConfig 통해 설정한다.
public class MemberApp {
  public static void main(String[] args) {
    AppConfig appConfig = new AppConfig();
    MemberService memberService = appConfig.memberService();

    Member member = new Member(1L, "memberA", Grade.VIP);
    memberService.join(member);

    Member findMember = memberService.findMember(1L);
    System.out.println("new member = " + member.getName());
    System.out.println("find Member = " + findMember.getName());

  }
}

☑️ DI : 의존 관계 주입

클라이언트인 memberServiceImpl의 의존관계를 외부appConfig에서 주입해준다.
👉 의존관계를 외부에서 주입

☑️ 관심사의 분리

객체를 생성하고 연결하는 역할과 실행하는 역할이 명확히 분리되었다.

📒 DIP 원칙 따름

📌 추상화에 의존해야한다.
클라이언트인 MemberServiceImpl은 실제 구현객체를 모르고 역할(인터페이스)에만 의존한다.

클래스 다이어그램**

회원 객체 다이어그램

📌 할인 정책 변경하기

📄 appConfig 코드만 변경

public class AppConfig {

  private static DiscountPolicy discountPolicy() {
    return new RateDiscountPolicy(); //OCP 원칙 지킴!! 
  }
}

👉 discountPolicy의 메서드 코드만 수정

💻 실제 동작
OrderApp에서 가격을 20000원으로,

public class OrderApp {
  public static void main(String[] args) {
  
    AppConfig appConfig = new AppConfig();
    MemberService memberService = appConfig.memberService();
    OrderService orderService = appConfig.orderService();

    Long memberID = 1L;
    Member memberA = new Member(memberID, "memberA", Grade.VIP);
    memberService.join(memberA);

    Order order = orderService.createOrder(memberID, "itemA", 20000);

    System.out.println("order = " + order);

  }
}

👉 10%할인 정책이 적용된것을 확인할 수 있다.

order = Order{memberID=1, itemName='itemA', itemPrice=20000, discountPrice=2000}

☑️ 사용과 구성의 분리

  • 애플리케이션을 사용영역과 객체를 생성하고 구성하는 역할로 변경할 수 있었다.

애플리케이션의 분리된 영역

📒 OCP 원칙 따름

📌 개방 폐쇄 원칙
👉 기존 코드를 변경하지 않고, 기능을 확장할 수 있었다.

할인 정책 변경 시

😊조금 정리해볼까요?😊

너무 많은 관심과 역할 : <클라이언트인 주문 서비스 구현체>
- 주문 기능 수행
- 할인 정책도 수행
appConfig를 통해서 
1. 실행하는 사용영역과 객체를 생성하고 연결하는 구성부분을 분리했다.
2. 각 코드의 관심사와 역할을 분리하였다. 
3. appConfig를 통해 의존관계를 주입하면서 
👉 클라이언트는 추상화에 의존하는 DIP를 지키게 되었다.
👉 기능 변경시 기존 코드를 변경하지 않아도 되는 OCP를 지키게 되었다. 
👍 너무 많은 관심은 좋지 않아.
→ 관심을 줄여서 "책임을 명확하게"

👏마무리하면서👏

스프링 조금 재미있을지도? 
profile
24년도까지 프로젝트 두개를 마치고 25년에는 개발 팀장을 할 수 있는 실력이 되자!

0개의 댓글