스프링 기본 sec3. 객체지향원리적용

고라니·2021년 9월 6일
0

스프링기본

목록 보기
3/5

section2에서 작성했던 순수 java코드의 여러 문제를 발견했었다. 리팩토링하면서
SOLID 원칙 5가지를 어떻게 지키는지 포커스를 두며 공부할 것.
내용 및 다이어그램 및 코드 부분 인용의 모든 출처는 김영한님의 인프런 스프링 강의입니다.

새로운 할인 정책 적용과 문제점

고정 할인 -> 비율 할인으로 변경하려고 하면?

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

새로 개발한 정률 할인 정책을 적용하려고 하니 클라이언트 코드인 주문 서비스 구현체도 함께 변경해야함 -> OCP 위반

주문 서비스 클라이언트가 인터페이스인 DiscountPolicy 뿐만 아니라, 구체 클래스인
FixDiscountPolicy 도 함께 의존 -> DIP 위반

변경해보자

1. 인터페이스에만 의존하도록 변경

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

이렇게만 하면 final은 무조건 초기화를 시켜줘야 되기 때문에 그냥 실행하면 무시무시한 NPE 발생.
따라서 누군가 클라이언트인 OrderServiceImpl 에 DiscountPolicy 의 구현 객체를
대신 생성하고 주입해주어야 한다.


2. AppConfig 등장
애플리케이션의 전체 동작 방식을 구성(config)하기 위해, 구현 객체를 생성하고, 연결하는 책임을 가지는 별도의 설정 클래스를 만들 것.

public class AppConfig {
   public MemberService memberService() {
   	return new MemberServiceImpl(new MemoryMemberRepository());
 }
   public OrderService orderService() {
     return new OrderServiceImpl(
       new MemoryMemberRepository(),
       new FixDiscountPolicy());
   }
}
/*참고 - 리팩토링 전 코드다*/
/*클라이언트 코드에는 생성자를 추가한다.*/
public class MemberServiceImpl implements MemberService {
   private final MemberRepository memberRepository;
   public MemberServiceImpl(MemberRepository memberRepository) {
 	this.memberRepository = memberRepository;
   }
   기능기능기능
}
  • AppConfig는 애플리케이션의 실제 동작에 필요한 구현 객체를 생성한다.
  • AppConfig는 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해서 주입(연결)해준다
  • MemberServiceImpl 은 이제부터 의존관계에 대한 고민은 외부(AppConfig)에 맡기고 실행에만 집중하면 된다.

3. AppConfig 리팩토링
AppConfig.java 의 코드를 보면 중복이 있고, 역할에 따른 구현이 한눈에 보이지 않음.

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

리팩토링 후 :

  • new MemoryMemberRepository() 이 부분이 중복 제거되었다.
    이제 MemoryMemberRepository 를 다른 구현체로 변경할 때에는 한 부분만 변경하면 됨.
  • AppConfig 를 보면 역할과 구현 클래스가 한눈에 들어온다.
    애플리케이션 전체 구성이 어떻게 되어있는지 빠르게 파악 가능하다.

클래스 다이어그램

  • appConfig 객체는 memoryMemberRepository 객체를 생성하고 그 참조값을 memberServiceImpl 을 생성하면서 생성자로 전달한다.
  • 클라이언트인 memberServiceImpl 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 같다고 해서 DI(Dependency Injection) 우리말로 의존관계 주입 또는 의존성 주입이라 한다.


결국에는?

DIP : ( 인터페이스 변수 선언 만 하고 끝나기 때문에 추상에만 의존하게 됨 )
OCP : ( AppConfig는 당연히 바꾸긴 하는데, 클라이언트 코드는 안바꾸잖아 )

결국 둘다 지키게 됨!!!!!


정리

전체 흐름 정리 ,좋은 객체 지향 설계의 5가지 원칙의 적용 부분 (앞내용 정리긴 함)
이부분은 매우 중요하니 블로그를 보는 것 보다는 pdf 및 강의로 복습 하는것이 좋다.ㅎㅎ


IoC

OrderServiceImpl 보필요한 인터페이스들을 호출하지만 어떤 구현 객체들이 실행될지 모른다.
이 제어권들이 모두 AppConfig로 넘어간 상황. 심지어 OrderServiceImpl 도 AppConfig가 생성함.

프레임워크 vs 라이브러리 (갑자기?)

프레임워크가 내가 작성한 코드를 제어하고, 대신 실행하면 그것은 프레임워크가 맞다. (JUnit)
반면에 내가 작성한 코드가 직접 제어의 흐름을 담당한다면 그것은 프레임워크가 아니라 라이브러리다.
Junit 같은 경우를 보면 , JUnit으로 테스트 작성시 @Test 에 개발자는 로직만 작성하고, 실행 및 제어권은 JUnit이 가져감.
자신만의 라이프 사이클로다가 ( , @BeforeEach , @AfterEach 애노테이션 등을 활용)

profile
공부를 열심히 하는 학부생

0개의 댓글