Skip to main content
soso01 blog
  1. Posts/

오브젝트 10장 상속과 중복 코드

·4 mins

01. 상속과 중복 코드 #

DRY 원칙 #

  • 중복 코드는 변경을 방해한다.
    • 중복코드는 코드를 수정하는 데 필요한 노력을 몇 배로 증가 시킨다.
    • 어떤 코드가 중복인지 찾고, 중복 코드의 묶음을 찾았다면 찾아낸 모든 코드를 일관되게 수정해야 한다.
    • 모든 중복 코드는 개별적으로 테스트해서 동일한 결과는 내놓는지 확인해야 한다.
  • 중복 여부를 판단하는 기준은 변경이다.
    • 요구사항이 변경됐을 때 두 코드를 함께 수정해야 한다면 이 코드는 중복이다.
    • 신뢰할 수 있고 수정하기 쉬운 소프트웨어를 만드는 효과적인 방법 중 하나는 중복을 제거하는 것이다.
    • DRY (Don’t Repeat Yourself)

중복과 변경 #

  • Call, NightlyDiscountPhone 예시(313p)
  • 중복 코드는 새로운 중복코드를 부른다.
    • 중복 코드를 제거하지 않은 상태에서 코드를 수정할 수 있는 유일한 방법은 새로운 중복 코드를 추가하는 것뿐이다.
    • 새로운 중복코드를 추가하는 과정에서 코드의 일관성이 무너질 위험이 항상 도사리고 있다.
    • 중복 코드가 늘어날수록 애플리케이션은 변경에 취약해지고 버그가 발생할 가능성이 높아진다.

상속을 이용해서 중복 코드 제거하기 #

  • 상속을 염두에 두고 설계되지 않은 클래스를 상속을 이용해 재사용하는 것은 생각보다 쉽지 않다.
    • 개발자는 재사용을 위해 상속 계층 사이에 무수히 많은 가정을 세웠을지도 모른다.
    • 그 가정은 코드를 이해하기 어렵게 만들뿐만 아니라 직관에도 어긋날 수 있다.
    • 요구사항과 구현 사이의 차이가 크면 클수록 코드를 이해하기 어려워진다.
  • 실제 프로젝트에서 마주치게 될 클래스의 상속 계층은 매우 깊을 것이다.
    • 깊고 깊은 상속 계층의 계단을 하나 내려올 때마다 이해하기 어려운 가정과 마주한다면?
  • 상속을 이용해 코드를 재사용하기 위해서는 부모 클래스의 개발자가 세웠던 가정이나 추론 과정을 정확하게 이해해야 한다.
    • 이는 자식 클래스 작성자가 부모 클래스의 구현 방법에 대한 정확한 지식을 가져야 한다는 것을 의미한다.
  • 상속은 결합도를 높인다.
    • 상속이 초래하는 부모 클래스와 자식 클래스 사이의 강한 결합이 코드를 수정하기 어렵게 만든다.

02. 취약한 기반 클래스 문제 #

  • 상속은 자식 클래스와 부모 클래스의 결합도를 높인다.
    • 이 강한 결합도로 인해 자식 클래스는 부모 클래스의 불필요한 세부사항에 엮이게 된다.
    • 이처럼 부모 클래스의 변경에 의해 자식 클래스가 영향을 받는 현상을 취약한 기반 클래스 문제라고 한다.
  • 상속 관계를 추가할수록 전체 시스템의 결합도가 높아진다.
    • 상속은 자식 클래스를 점진적으로 추가해서 기능을 확장하는 데는 용이하지만 높은 결합도로 인해 부모 클래스를 점진적으로 개선하는 것은 어렵게 만든다.
    • 취약한 기반 클래스 문제는 캡슐화를 약화시키고 결합도를 높인다.

불필요한 인터페이스 상속문제 #

  • 상속받은 부모 클래스의 메서드가 자식 클래스의 내부 구조에 대한 규칙을 깨뜨릴 수 있다.
  • 자바의 Stack은 Vector 클래스를 상속한다.
    • Stack에서 push가 아닌 Vector의 add 메서드를 사용하면 에러를 유발할 수 있음. (스택의 가장 뒤가 아닌 앞에 데이터가 추가됨)
  • Stack을 사용하는 개발자들이 Vector에서 상속받은 메서드를 사용하지 않으면 된다고 생각할 수는 있다.
    • 하지만 인터페이스는 설계는 제대로 쓰기엔 쉽게, 엉터리로 쓰기엔 어렵게 만들어야 한다.
    • Stack 개발자 한 사람의 일시적인 편의를 위해 인터페이스를 사용해야 하는 무수한 사람들이 가슴을 졸여야 하는 상황을 초래하는 것은 정당화하기 어렵다.

메서드 오버라이딩의 오작용 문제 #

  • 자식 클래스에서 메서드를 오버라이딩할 때, 부모 클래스의 다른 메서드에서 오버라이딩 하는 메서드를 호출하는지 파악해야 한다.
  • 부모 클래스가 자신의 메서드를 사용하는 방법에 자식 클래스가 결합될 수 있다.

부모 클래스와 자식 클래스 동시 수정 문제 #

  • 클래스를 상속하면 결합도로 인해 자식 클래스와 부모 클래스의 구현을 영원히 변경하지 않거나, 자식 클래스와 부모클래스를 동시에 변경하거나 둘 중 하나를 선택할 수 밖에 없다.

03. Phone 다시 살펴보기 #

추상화에 의존하자 #

  • 자식 클래스가 부모 클래스의 구현이 아닌 추상화에 의존하도록 만들어라.

차이를 메서드로 추출하라 #

  • 중복 코드 안에서 차이점은 별도의 메서드로 추출하라
    • ‘변하는 것으로부터 변하지 않는 것을 분리하라’, ‘변하는 부분을 찾고 이를 캡슐화하라’ 라는 조언을 메서드 수준에서 적용한 것이다.
  • 예시의 Phone, NightDiscountPhone의 경우 계산한 요금을 더하는 부분은 공통화하고, 계산하는 부분을 추출한다.

중복 코드를 부모 클래스로 올려라 #

  • Phone과 NightlyDiscountPhone의 공통 부분을 부모 클래스로 이동시킨다.
    • 동일한 메서드인 calculateFee는 추상 클래스로 이동하고, calculateCallFee만 자식클래스에서 각자 구현한다.

추상화가 핵심이다 #

  • 공통 코드를 이동시킨 후에 각 클래스는 서로 다른 변경의 이유를 가지게 된다.
    • AbstractPhone - 전체 통화 목록의 계산 방법이 변경되었을 때
    • Phone - 일반 요금제의 통화 한 건을 계산하는 방식이 변경되었을 때
    • NightlyDiscountPhone - 심야 할인 요금제의 통화 한 건을 계산하는 방식이 변경되었을 때
    • 단일 책임 원칙 준수
  • 클래스들이 추상화에 의존하기 때문에 의존성 역전 원칙도 준수하며, 새로운 요금제를 추가하기도 쉽다.

04. 차이에 의한 프로그래밍 #

  • 기존 코드와 다른 부분만을 추가함으로써 애플리케이션의 기능을 확장하는 방법을 차이에 의한 프로그래밍이라고 부른다.
  • 상속을 이용하면 이미 존재한느 클래스의 코드를 쉽게 재사용할 수 있기 때문에 애플리케이션의 점진적인 정의가 가능해진다.
  • 차이에 의한 프로그래밍의 목표는 중복 코드를 재사용하는 것이다.
    • 중복 코드 제거와 코드 재사용은 동일한 행동을 가리키는 서로 다른 단어다.
    • 중복을 제거하기 위해서는 코드를 재사용 가능한 단위로 분해하고 재구성해야 한다.
    • 코드를 재사용하기 위해서는 중복 코드를 제거해서 하나의 모듈로 모아야 한다.
    • 따라서 중복 코드를 제거하기 위해 최대한 코드를 재사용해야 한다.
  • 재사용 가능한 코드란 심각한 버그가 존재하지 않는 코드다.
    • 코드를 재사용하면 코드의 품질은 유지하면서도 코드를 작성하는 노력과 테스트는 줄일 수 있다.
  • 객체지향 세계에서 중복 코드를 제거하고 코드를 재사용할 수 있는 가장 유명한 방법은 상속이다.
    • 하지만 상속의 오용과 남용은 애플리케이션을 이해하고 확장하기 어렵게 만든다.