[spring] 객체지향의 원리 적용 (스프링 기본편 by 김영한)

su_y2on·2022년 1월 16일
0

Spring

목록 보기
14/30
post-thumbnail

객체지향의 원리 적용

이전까지 구축한 회원, 주문, 할인 기능을 하나씩 살펴보며 객체지향적으로 설계되었는지 그렇지 않았다면 객체지향의 원리를 적용해서 수정해보도록 하겠습니다


할인정책변경

기존코드에서 할인정책을 모든 vip회원을 일정금액 할인해주는 FixDiscountPolicy에서 구매금액의 일정 비율만큼 할인해주는 RateDiscountPolicy로 변경해보겠습니다.

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

여기서 문제가 발생하는데요. 원래 설계대로라면 지금같이 OrderServiceImpl는 변경이 일어나면 안됩니다. OrderServiceImpl는 DiscountPolicy라는 추상체(인터페이스)만을 바라보고있기때문에 구현체의 변경(Fix or Rate)으로 OrderServiceImpl의 코드변경은 일어나면 안되기 때문입니다. 결국 OCP를 위반됩니다




지금같은 문제가 발생하는 이유는 애초에 기대했던 대로 구현하지 못했기 때문입니다

public class OrderServiceImpl implements OrderService {

      ...
      
      private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
      
      ...
    }
      

위의 원래 코드를 보면 OrderServiceImpl은 DiscountPolicy인 추상체에 의존하고 있지만 오른편에 FixDiscountPolicy인 구현체에도 의존을 하고있습니다. (DIP위반)

즉 의도와는 다르게 아래와 같은 관계를 형성하고있는 것입니다.


지금 까지 상황을 정리해보면 OrderService가 구현체에 의존을 하고있기 때문에(DIP위반) 할인정책을 바꿀때 코드의 변경이 일어나게 된 것입니다.(OCP위반)



코드수정

다시 처음 의도했던 의존관계를 갖도록 코드를 수정해보겠습니다. 이제 OrderServiceImpl은 MemberRepository와 DiscountPolicy 처럼 추상체들만 바라보게 됩니다. 이 추상체들에 구현체들을 넣어주는 것은 따로 관리자처럼 따로 빼서 처리해줘야합니다!

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); // 단일책임원칙 잘지켜짐

        // cmd + p : 파라미터 확인
        return new Order(memberId, itemPrice, itemName, discountPrice);

    }
}




AppConfig : 의존성 주입

이렇게 따로 의존성을 주입해줄 AppConfig를 만들어줍니다. 지금 같은 경우는 MemoryMemberRepository와 FixDiscountPolicy를 구현체로 선택한 경우입니다.

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



이제 테스트를 다시 진행해보도록 하겠습니다. 아래 테스트코드처럼 매번 테스트시에 AppConfig를 만들고 appConfig에서 memberService와 orderService를 빼서 쓰는 방식으로 진행하면 되겠습니다.

public class OrderServiceTest {

    MemberService memberService;
    OrderService orderService;

    @BeforeEach
    public void beforeEach() {
        AppConfig appConfig = new AppConfig();
        memberService = appConfig.memberService();
        orderService = appConfig.orderService();
    }


    @Test
    void createOrder() {
        Long memberId = 1L;
        Member member = new Member(memberId, "memberA", Grade.VIP);
        memberService.join(member);

        Order order = orderService.createOrder(memberId, "itemA", 10000);
        Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
    }
}

그러면 orderService같은 경우 생성자를 통해 MemoryMemberRepository와 FixDiscountPolicy가 각각 설정된 OrderService객체가 반환될 것입니다!

0개의 댓글