객체지향원리 이해

강한친구·2022년 4월 6일
0

Spring

목록 보기
1/27

객체지향의 원칙알고 보면 좋다.

스프링의 역할

완벽한 OCP, DIP를 수행하기는 쉽지 않다.

수업을 따라오면서 지금까지 작성한 코드는 다음과 같다.

서비스 = Member, Order, Grade
도메인 = MemberServiceImpl, OrderServiceImpl
리포지토리 = MemoryMemberRepository

이를 통해 우리는 회원을 등록할 수 있고, 주문을 생성할 수 있고 이를 메모리에 저장할 수 있다.

그리고 등급에 따른 고정할인을 제공하는 FixDiscount를 작성하였다. 이 discountpolicy는 orderServiceImpl에 적용된다.

다음의 코드를 보자

public class OrderServiceImpl implements OrderService {
 private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
}

이 경우, 만약 discountPolicy가 새로 개발한 RateDiscount로 변경된다면, orderImpl에서 해당 부분을 바꿔치기하면될것이다.

public class OrderServiceImpl implements OrderService {
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
 private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}

이때, orderServiceImpl은 DisocountPolicy라는 인터페이스 (추상화), 그리고 동시에 Rate / Fix 라는 구체적 클래스(구현)에 의존하고 있다.

이는 원칙에 위배되는 일이다. 따라서 우리는 이를 해결해줘야한다.

원칙대로 수정

public class OrderServiceImpl implements OrderService {
 //private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
 private DiscountPolicy discountPolicy;
}

이렇게 수정하면 뭐가 나올까?

이 인터페이스는 아무것도 포인팅하지않는다. 이는 NPE를 야기한다.

이를 해결하기 위해서 애플리케이션 작동방식을 총괄하는 Appconfig를 사용해보자.

Appconfig.class

public class AppConfig {

    
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }
    
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
    
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
    
    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }
}
  1. MemberServicesms memberRepository를 쓰는 memberServiceImpl을 불러온다.

  2. OrderService는 memberRepository, discountPolicy를 쓰는 OrderServiceImpl을 불러온다.

  3. MemberRepository는 MemoryMemberRepository를 불러온다.

  4. DiscountPolicy는 RateDiscountPolicy를 불러온다.

이런식으로 별도의 Config 클라스를 통해서 각 메소드들의 호출관계를 조정해줄 수 있다.

이는 기존의 객체디자인 패턴에서도 종종 보이던 패턴이다.

주입

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

이런식으로 인터페이스에만 의존하도록 변경된 각 클래스들은, 생성자를 통해서 값을 주입받으면 된다.

AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
OrderService orderService = appConfig.orderService();

실행순서
1. MemberService 객체에 앱 컨피그.memberService를 호출한다.
2. config의 memberService는 MemberServiceImpl(memberRepository(new MemoryMemberRepository))를 호출한다.
3. 이는 MemberServiceImpl의 생성자를 통해 MemberService안의 memberRepository와 연동된다.

예시)

private final MemberRepository memberRepository;

   public MemberServiceImpl(MemberRepository memberRepository) {
       this.memberRepository = memberRepository;
   }
  1. Order역시 같은 원리로 작동한다. 이렇게 각 객체에 구체적 할당을 해주는 역할을 하는것이 Config이다.

    스프링으로 전환

    하지만 이는 스프링으로 해결한 DI가 아니고 순수한 자바코드로 한 일이다. 이는 추후에 설명할 싱글톤문제에서 자유롭지 못한 방식이다. 따라서 이는 Spring으로 전환하여 해결해야한다.
    @Configuration
    public class AppConfig {
    @Bean
    public MemberService memberService() {
    return new MemberServiceImpl(memberRepository());
    }
    이런식으로 Configuration, Bean Annotation을 붙이는것으로 이를 해결 할 수 있다. 그리고 실제 적용은
         ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
         MemberService memberService = applicationContext.getBean("memberService", MemberService.class);

이런식으로 ApplicationContext로 호출하고, 세부값들을 membberService에 넣어주는식으로 처리하면 된다.

이러면 스프링에서 각 클래스들을 Bean화 하여 컨테이너에 등록하고 알아서 관리해준다.

0개의 댓글