다형성

  • 메시지 수발신, 다형성을 생각해보면 코드의 의존성과 실행 시점의 의존성이 다를 수 있다는 것을 알 수 있다.
  • 클래스 사이의 의존성과 객체 사이의 의존성은 동일하지 않을 수 있다. 물론 이 간극이 달라질수록 이해하기 어려워진다.

차이에 의한 프로그래밍

  • 상속에 대해서 더 자세히 살펴보면 클래스를 수정하지 않고 그냥 그대로 사용할 수 있는 가장 쉬운 방법은 상속이다.
  • 부모 클래스와 다른 부분만 추가해서 새로운 클래스를 쉽고 빠르게 만드는 방법을 ‘차이에 의한 프로그래밍이라고 한다.’
  • 물론 상속이 재활용에 가장 쉬운 방법일 수 있지만 단순히 ‘재활용‘만을 위해서 택한다면 큰 오해다.
  • 재사용에 더 좋고 확장성이 좋은, 사이드 이펙트가 없는 방법이 있다.

상속, 인터페이스

  • 인터페이스는 객체가 이해할 수 있는 메시지 목록을 정의한다.
  • 상속 역시 재사용에 목표가 있을 수도 있지만 상속을 통해 자식 클래스는 자신의 인터페이스에 부모의 인터페이스를 포함하게 되고, 부모가 수신할 수 있는 메시지를 모두 수신할 수 있다.
  • 이런 아이디어로 다형성이 성립하게 된다.
  • 다형성이란 메시지를 수신했을 때 객체의 타입에 따라 응답할 수 있는 능력을 의미한다.
  • 지연 바인딩(Lazy binding), 동적 바인딩(Dynamic binding)이라고 부른다.
  • 반대로 컴파일 시점에 실행할 함수나 프로시저를 결정하는 것을 초기바인딩 또는 정적바인딩이라고 한다.

구현 상속, 인터페이스 상속

  • 구현 상속(implementation inheritance)는 서브 클래싱(subclassing)이라고 하며, 순수하게 코드 재사용을 목적으로 한다.
  • 인터페이스 상속(interface inheritance)는 서브 타이핑(subtyping)이라고 하며, 다형적인 협력을 위해 부모 클래스와 자식 클래스가 인터페이스를 공유할 수 있도록 상속을 이용하는 것을 의미한다.

추상화

  • 구현 상속, 인터페이스 상속을 위해서는 추상화를 사용하면 된다.
  • 추상화를 중심으로 코드의 구조를 설계하면 유연하고 확장 가능한 설계를 만들 수 있다.
  • 설계를 통해서 구체적인 상황에 결합되는 것을 방지할 수 있기 때문이다.
  • 이는 컨텍스트 독립성을 통해서 유연한 설계를 할 수 있게 해준다.
  • 물론 트레이드 오프가 있을 수 있다. 모든 코드에는 합당한 이유가 있어야 한다.

재사용

  • 상속 대신 재사용을 위해서는 다른 방법이 더 나을 수 있다.
  • Composition은 다른 객체의 인스턴스를 자신의 인스턴스 변수로 포함해서 재사용하는 방법을 의미한다.

상속

  • 상속을 재사용 목적만으로 사용하기는 위와 같은 좋은 방법이 있어서 문제가 있다.
  • 더 나아가면 아래와 같은 문제들이 있다.
    • 캡슐화 위반
    • 설계에서의 유연성을 앗아간다.
  • 결과적으로 부모 클래스 구현이 자신 클래스에게 노출되기 때문에 캡슐화가 약화되고
  • 부모와 자식 간 강결합이 형성된다.
  • 또한 부모-자식 관계가 컴파일 타임에 결정되므로 설계가 유연하지 않다.

합성(Composition)

  • 인터페이스에 정의된 메시지를 통해서만 코드를 재사용하는 방법을 합성이라고 한다.
  • 합성은 두 가지 상속의 문제를 해결한다.
    • 인터페이스에 정의된 메시지를 통해서만 재사용 가능하기 때문에 구현을 효과적으로 캡슐화할 수 있다.
    • 의존하는 인스턴스를 교체하는 것이 비교적 쉽기 때문에 설계를 유연하게 한다.