Interface vs. Implementation

최완식·2022년 11월 17일
0
post-thumbnail

인터페이스를 사용해야 하는가? 그냥 실질적인 내용이 담긴 구현체를 사용해야 하는가?

모든 것이 인터페이스여야 한다는 주장

다른 클래스의 메서드를 절대로 직접 호출하면 안된다. 모든 것을 인터페이스로 만들어라.

  • 왜?: OO에서는 디커플링이 "언제나 가장(?)" 중요하기 때문이다.
  • 인터페이스를 추가하면 굉장히 지저분하게 변한다.
  • 혹시라도 다른 구현이 생길수도 있잖아요?
    • 생길수도 안생길 수도 있다.
    • 필요할 때 만드는 것이 좋다.
    • 실세상도 모든 미래를 대비하면서 구축되어 있지 않다. 왜 소프트웨어에서만 그렇게 하는가?

인터페이스 : 구현 = 1 : 1은 이상하다.

  • OO에서의 인터페이스 정의 (이 강의에서)
    • 상태도 메서드 구현도 없는 순수 추상 클래스
    • public 메서드 시그내처만 모아둔 것
    • C 언어의 header 파일 같은 존재
  • OO에서의 인터페이스: 주 용도를 기준으로
    • 함수 포인터처럼 사용 가능: 다형성
    • 다중 상속을 흉내낼 수 있음: 다형성
    • 변화에 대비해 결합도를 낮출 수 있음(단, 필요한 경우만): 다형성
  • 다형성 없는 인터페이스는 없다.가 일반적인 주장

인터페이스의 잘못된 이해

  • 왜 인터페이스만 중요시 하는가?
  • 이 진영에서 성서처럼 받드는 책: GoF의 디자인 패턴
  • 책 내용은 괜찮나 곡해하는 사람들이 문제임
  • "Programming to an Interface, not an Implementation."
  • 아 무조건 Java interface 사용하면 되는구나! ❌
  • 하지만 여기서 interface는 그 의미로 적은게 아니다.
    • 사실 interface라는 용어자체를 여러곳에서 사용하기 때문에 발생한 문제라 볼 수도 있다.

Java의 Interface가 아닌 근거

  1. 해당 책은 C++로 작성되어 있음, 즉 interface를 지원하지 않는다.
  2. 다형성이 인터페이스에 의존한다고 명시되어 있다.
    • 상속에서 동작의 재사용만 말하는 것은 반쪽짜리이다.
    • 상속을 통해 자식이 따라야하는 공통 interface를 정의하는 것도 중요하다.
    • 그 이유는 다형성이 공통 interface없이는 존재할 수 없기 때문이다.
  3. 부모의 다형적 메서드 시그내쳐라는 의미로 사용했다.
    • 추상 클래스에 있는 interface만 사용하면 이러한 장점이 있다.
      • 클라이언트는 자기가 사용하는 개체의 타입이 뭔지 몰라도 된다.
      • 클라이언트는 개체를 구현하는 클래스에 대해 몰라도 된다.
      • 단, 추상 클래스가 강제하는 interface만 따르면 된다.

즉, 여기서 말하는 interface는 자바에서 말하는 Interface가 아니라 메서드 시그니쳐 그 자체를 말한다.

인터페이스에 대해 프로그래밍하라는 의미

  • 상위 클래스를 사용할수록 결합도가 줄어든다.
  • 함수 == 블랙박스 라는 말에 다형성이 추가된 것에 불과
    • 함수는 함수 안에서 무엇이 일어나는지 알려고 하지 말아라.
      • 파라미터와 반환형만, 즉 시그내쳐만 보고 가자.
    • 클래스는 구체 클래스에 의존하지 말고, 부모에서 정의한 다형적 메서드에 의존하자.
  • 핵심: 다형성을 가진 일반적 메서드 시그내처를 정의
    • 무조건 Java의 interface를 만들어야 한다? ❌

협업 시 가장 중요한 목표는 실수 예방

  • 소프트웨어 개발은 협업 환경
  • 모두가 직관적으로 이해할 수 있는 방법은 실수를 줄인다.
    • 주관성이 그나마 적음
    • OO의 캡슐화
  • 다형성, 포인터, 재귀 함수 등이 이해하기 어려운 이유
    • 직관성이 줄어듦
    • 추상적
  • 기본적으로 추상화를 안하는게 실수가 적다.

인터페이스의 올바른 정의

  • 이런 기조를 기반으로 정의를 한다면, (주류 언어, 주 용도)
  • 상태도 구현도 없이 메서드 시그내처만 있는 추상 클래스
  • 함수 포인터처럼 또는 다중 상속 대신 사용가능 (다형성)
  • 이외의 주관적 의견은 장점이 확실히 보일 때 받아들이자.

이클립스 API와 인터페이스 - 디커플링을 잘한 예

  • 이클립스: Java IDE
  • 많은 사람들이 사용하여 이클립스 API를 사용한 많은 도구들이 있음
  • 근데, 이 API 버전 바뀔 때마다 메서드 시그내처가 바뀐다면?
    • 많은 도구들이 고장남
    • 다 바꿔줘야 함
  • 이러한 변화가 적어야 함

설계에 많은 시간 투자

  • 추후 변화에 강건하게 만들기 위해 꼼꼼히 설계함
  • 하지만 바뀌지 않는 API는 발전도 없음
  • 그렇기에 다음 두 Java 키워드에 의미를 부여하고 약속함
    • interface: 절대로 바뀌지 않음
    • class: 언제든 바뀔 수 있음

하지만 interface로 발전시켜야 했다.

  • 아무리 설계를 잘해도 모든 미래를 예측할 수는 없다.
  • 없는 동작을 추가해야 할 일이 생긴다.
  • 추가하게 되면, 해당 인터페이스를 사용하는 모든 클래스가 컴파일 안된다.

해결법

  • 이전버전과 호환되는 새로운 인터페이스를 만들었다.
    • 뒤에 숫자를 붙이는 방식
  • 이러면 새 버전이 나와도 기존 클라이언트 코드는 동작한다.

장단점

  • 새로 이클립스 API를 사용하려는 사람이 들어왔다.
  • 1, 2, 3? 뭘써야 하지?
  • 뒤에 숫자 붙이는 이름이 좋은 걸까?
  • 사실 이런 프로젝트를 다시 마주하는 것이 어려움. 설계에 이만큼 투자를..?
장점단점
- interfaceclass의 의미를 확실히 분리
- 기존 코드를 망가트리지 않음
- 인터페이스가 깔끔하게 유지되지 않음
- 당연히 변화에 느림 (예전에는 애초에 느려서 별문제 아니었음)

현재에도 의미는 있다.

  • 많은 사람들이 의존하는 라이브러리를 만든다면 고려해볼만 하다.
    • 클라이언트가 회사 내부 팀이 아닌 경우
  • 하지만 요즘은 발전 속도가 더 중요하게 되었다.
    • 인터넷 발달로 새버전 배포가 용이
    • 보안 패치를 더 자주해야 함
  • 다른 방향으로 가는중..

실무에서 방향

  • 회사 자체 개발팀 소유
  • 프로그래머 100+ 명
  • 총 10개의 제품 팀
  • 코어팀
    • 10개의 팀이 사용하는 핵심 라이브러리를 만듦
    • 해당 라이브러리는 내부에서만 사용
  • 코어팀에서 클래스 혹은 인터페이스의 public 메서드 시그내처를 바꿈
    • 10개의 팀의 개발 일정에 맞춰야 함
    • 팀하고 상의를 해야 함
  • 이런 상황에서 인터페이스와 커플링해두면 조금더 낫다.

핵심은 클라이언트와의 약속

  • 단순히 구체 클래스와의 결합도를 줄이냐 마냐의 문제가 아니다.
  • 무엇을 변경하고 무엇을 변경하지 않을지에 대한 약속
  • 그 약속을 깨야할 일이 생긴다면 일정조율과 같은 것이 중요
  • 이클립스 api와 같은 방식의 약속도 그 중 하나

요즘은 업데이트에 익숙

  • 요즘은 버전업 속도가 빨라졌다.
  • 그래도 이것이 일상의 일부라 생각하는 경향이 있다.
  • 새로운 기능이나 breaking 변화는 새 버전에만 추가
  • 버전 별로 지원 기간을 명시
    • 지원 기간동안 중요한 업데이트를 모든 버전에 적용해야 함
    • 지원 기간이 끝나면 다음 버전으로 옮겨야 함
  • 업데이트 후 오작동을 고치는 것은 클라이언트의 몫
  • 고통을 줄이기 위한 방법들
    • migration 가이드 제공
    • LTS(Long-Term Support) 버전 제공

실용적인 인터페이스 사용법 정리

  • 기본적으로 클래스 사용
  • 기존: 다음의 경우에만 인터페이스 사용
    • 함수 포인터
    • 다형성 있는 다중 상속이 필요한 경우
  • 추가: 변화에 대비할 필요가 있다면 커플링을 줄이려 사용할 것
    • 내 클래스에 의존하는 코드들을 쉽게 바꿀 수 없는 경우
      • 라이브러리

변화에 대한 대비란?

쉽게 바꿀 수 없는 경우에 대한 대비

  • 쉽게 바꿀 수 있다면, 그 때 인터페이스로 처리하면 된다.
  • 시스템이 크지 않거나
  • 내 라이브러리를 사용하는 외부 클라이언트의 수가 적거나
  • 여러 버전을 특정 기간동안 지원할 수 있거나.

정리

  • 굉장히 주관적인 내용이다.
  • 양 극단에 위치한 것은 항상 잘못될 가능성이 높다.
  • 시간이 지난후에도 다수설이 그대로 유지되고 있다면, 특정 사람이 주장하는 것이 잘못될 가능성이 높다.
  • 의존성, 결합도에 대한 정의와 디커플링에서의 의존성 주입, 그리고 그의 장단점을 알아보았다.

Reference

profile
Goal, Plan, Execute.

0개의 댓글