관심사의 분리, DI

초보개발·2021년 12월 7일
0

Spring

목록 보기
5/37

관심사의 분리

Separation of Concerns, SoC
소프트웨어 상에서 구조를 패턴, 역할, 기능 등 각각 맞게 분야 별로 분리해서 작성하는 것을 말한다. 분리해서 작성할 때, 특성에 맞게 하나의 역할을 부여해서 작성해야 한다.

  • Spring에서 SoC를 통해서 결합도는 낮게, 응집도는 높이면서 프로그램에서 실세계를 반영하려 한다.
    • 클래스 간의 의존성을 낮추면 유지 보수와 객체의 재사용이 쉬워진다.
    • 모듈 내부 요소들 간의 기능적 관련성이 높으면 독립성이 높아지므로 재사용에 용이하다.

AppConfig

애플리케이션의 전체 동작 방식을 구성하기 위해 구현 객체를 생성하고 연결하는 책임을 가진 별도의 설정 클래스를 만들어 기존 코드의 문제점을 해결할 수 있다.

public class OrderServiceImpl implements OrderService{
    private final MemberRepo mr = new MemoryMemberRepository();
    private final DiscountPolicy dp = new FixDiscountPolicy();
    ...
}

이 코드에서 할인 정책을 변경할 경우 소스 코드도 변경되어야 하므로 OCP를 위배한다. 또한, 인터페이스와 그 구현체에도 의존하고 있으므로 DIP에도 위반을 하고 있다. 따라서 인터페이스에만 의존할 수 있도록 의존관계를 변경해야 한다. 저 클래스에서 특정 구현체를 설정하는 것이 아닌, 관리자같은 클래스(AppConfig)로 관리하도록 변경하면 된다.

AppConfig의 역할

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(); // 구현 클래스
    }
}
}
  • 애플리케이션의 실제 동작에 필요한 구현 객체 생성
    • MemberServiceImpl, MemoryMemberRepository, OrderServiceImpl, FixDiscountPolicy
  • 생성한 객체 인스턴스의 레퍼런스를 생성자 주입으로 연결해준다.
    • MemberServiceImpl -> MemoryMemberRepository
    • OrderServiceImpl -> MemoryMemberRepository, FixDiscountPolicy
  • 애플리케이션이 사용 영역과 객체를 생성하고 구성하는 영역으로 분리되었다.

    따라서 사용 영역의 코드는 변경할 필요가 없으며, 구성 영역의 코드만 변경하면 된다.

생성자 주입

  1. MemberServiceImpl
public class MemberServiceImpl implements MemberService {

    private final MemberRepository memberRepository; 

    public MemberServiceImpl(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    ...
}
  • 설계 변경으로 이 구현체는 MemoryMemberRepository를 의존하지 않는다.
  • 인터페이스인 MemberRepository에만 의존하므로 DIP 원칙을 준수하고 있다.
  • MemberServiceImpl은 생성자를 통해 어떤 구현체가 주입될지는 알 수 없다. (관심사가 아니다)
  • MemberServiceImpl의 생성자를 통해 어떤 구현체를 주입할지는 외부(AppConfig)에서 결정된다.
  • MemberServiceImpl은 의존관계에 대한 고민은 외부에 맡기고 실행에만 집중할 수 있게 되었다.

    관심사의 분리
    객체를 생성하고 연결하는 역할과 실행하는 역할이 명확히 분리 되었다.
    DI
    클라이언트인 memberServiceImpl의 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 같다고 해서 DI(Dependency Injection, 의존관계 주입)이라고 한다.

  1. 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;
      }
      ...
 }
  • 설계 변경으로 구현체인 FixDiscountPolicy를 의존하지 않으며 인터페이스인 DiscountPolicy만 의존한다.
  • MemberServiceImpl와 마찬가지로 실행에만 집중할 수 있게 되었다.
  • OrderServiceImpl에는 MemoryMemberRepository, FixDiscountPolicy 객체의 의존관계가 주입된다.

0개의 댓글