java-squid/effective-java

[아이템 64] 객체는 인터페이스를 사용하라

Closed this issue · 4 comments

[아이템 64] 객체는 인터페이스를 사용하라

저도 이거 보면서 DI 만 생각났네요.. 근데 확실히 인터페이스를 잘 쓰면 프로그램이 상당히 유연해지고, 코드를 짜는 습관에도 도움이 많이 되더라고요. 먼져 추상화 시키려하는 습관들이 생겨서

제가 Spring 을 전혀 몰라서, 덕분에 올려주신 링크 읽어보면서 공부를 할 수 있었는데,

"객체는 인터페이스를 사용해라" 에서 DI 가 떠오르신 이유가 무엇인지 좀더 설명해주실 수 있으신가요?

보니깐 ApplicationContext 에서 xml 에 정의된 내용에 따라,
클래스의 인터페이스로 선언된 객체에게 실제 클래스를 "Injecting" 하는 내용 같은데,
DI 를 하기 위해서는 인터페이스를 사용해야하기에 떠오르셨던건가요?

대화 흐름을 못따라가서 여쭈어봅니다.

토비의 스프링, 김영한님의 스프링 강의를 수강하면서 정리한 내용입니다.

제어의 역전 (IoC: Inversion of Control)

  • AppConfig가 등장 후 구현 객체는 자신의 로직을 실행하는 역할만 한다. 프로그램의 제어 흐름은 AppConfig가 담당한다.
    → orderServiceImpl은 필요한 인터페이스들을 호출하지만 어떤 구현 객체들이 실행될지 모름.
  • 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것
    제어의 역전(IoC)

프레임워크 vs 라이브러리

  • 프레임워크: 내가 작성한 코드를 제어, 대신 실행하면 프레임워크(JUnit)
  • 라이브러리: 내가 작성한 코드가 직접 제의 흐름을 담당. (객체 → JSON으로 바꾸기)

의존관계 주입(DI: Dependency Injection)

  • 인터페이스에 의존하기 때문에 실제 어떤 구현 객체가 사용될지는 모른다.
  • 의존관계는 정적인 클래스 의존 관계와 실행 시점에 결정되는 동적인 객체(인스턴스) 의존 관계 둘을 분리해서 생각해야 한다.

정적인 클래스 의존 관계

import 코드만 보고 의존관계를 쉽게 판단할 수 있다.

→ 정적인 의존관계는 애플리케이션을 실행하지 않아도 분석할 수 있다.
image

public class AppConfig {

    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    private MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }
    // 고정 할인 정책
    public DiscountPolicy discountPolicy() {
        return new FixDiscountPolicy();
    }
}

// OrderServiceImpl.class
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;
    }
}

이제 여기서 OrderSeviceImpl에서 고정 할인 정책이 아닌 할인율 정책으로 변경할 경우

public DiscountPolicy discountPolicy() {
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
}

로만 바꾸면 됩니다. 서비스 로직인 OrderServiceImpl에서 바꿀 필요가 없겠죠? 이건 자바로직이고 여기서 만약 스프링으로 전환한다면

@Configuration
public class AppConfig {

    @Bean
    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    @Bean
    public DiscountPolicy discountPolicy() {
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }
}

로 애너테이션으로 등록이 가능합니다. 김영한님 강의에서는 xml에서 애너테이션으로 바꾸는 추세라고 하셨고, xml을 쓴다고 하면 아마 레거시쪽이 많이 쓸 듯하네요.
IoC는 제어의 역전이며, DI는 스프링 프레임워크에서 지원하는 IoC의 형태라고 생각하시면 됩니다. 자세한건
인프런의 김영한님 강의 또는 토비의 스프링을 추천합니다!!