ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 오브젝트 - Chapter 10 상속과 코드 재사용
    Study 2023. 11. 24. 22:35
    • 객체지향 프로그래밍의 장점 중 하나는 코드를 재사용하기 용이하다는 것이다.
    • 전통적인 패러다임에서 코드의 재사용은 코드를 복사 후 수정하는 것이다.
    • 객체지향에서 코드는 일반적으로 클래스 안에 작성되기 때문에
      객체지향에서 클래스를 재사용하기 위한 전통적인 방법은 새로운 클래스를 추가하는 것이다.
    • 이번 장에서는 클래스를 재사용하기 위해 새로운 클래스를 추가하는 가장 대표적인 기법인 상속에 대해서 살펴본다.

    상속

    • 재사용 관점에서 상속이란 클래스 안에 정의된 인스턴스 변수와 메서드를 자동으로 새로운 클래스에 추가하는 구현 기법이다.

    합성

    • 새로운 클래스의 인스턴스 안에 기존 클래스의 인스턴스를 포함시키는 방법

    코드를 재사용하려는 강력한 동기 이면에는 중복된 코드를 제거하려는 욕망이 숨어 있다.
    중복 코드가 초래하는 문제점을 먼저 보고 가자.

    01 상속과 중복 코드

    DRY 원칙

    • 중복 코드는 변경을 방해한다. 이것은 중복 코드를 제거해야 하는 가장 큰 이유이다.
    • 프로그램의 본질은 비즈니스와 관련된 지식을 코드로 변환하는 것이다.
    • 지식은 항상 변하고 지식을 표현하는 코드도 항상 변경해야 한다.
    • 중복 코드가 가지는 가장 큰 문제는 코드를 수정하는데 필요한 노력을 몇 배로 증가시키는 것이다.
    • 중복의 판단 기준은 변경이다. 요구사항이 변경되었을 때 두 코드를 함께 수정해야 한다면 이 코드는 중복이다.
    • Don't Repeat Yourself 원칙을 지키자

    중복과 변경

    • 예시 코드에서 중복된 코드를 작성하고 변경 시 중복된 코드를 모두 수정해야 됨을 보여주고 있다.
    • 중복된 코드에 변경된 내용을 모두 반영하지 못 하면 변경이 모두 반영되지 못하는 결과를 초래할 수 있다.

    타입 코드 사용

    • 두 클래스를 하나로 합치고 각각 타입 코드를 부여하고 각 타입 코드에 따라 분기시켜 두 클래스를 합칠 수 있다.
    • 객체지향 프로그래밍 언어는 타입 코드를 사용하지 않고도 중복 코드를 관리할 수 있는 효과적인 방법을 제공한다.
      상속이 바로 그것이다.

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

    • 상속의 기본 아이디어는 이미 존재하는 클래스와 유사한 클래스가 필요하다면 코드를 복사하지 말고 상속을 이용해 코드를 재사용하라는 것이다.
    • 예시 코드를 통해서 보여주고 있는 것은
      상속을 이용해 코드를 재사용하기 위해서는 부모 클래스의 개발자가 세웠던 가정이나 추론 과정을 정확하게 이해해야 한다.
    • 이것은 자식 클래스의 작성자가 부모 클래스의 구현 방법에 대해 정확한 지식을 가져야 한다는 것을 의미한다.
    • 따라서 상속은 결합도를 높인다.
    • 상속이 초래하는 부모 클래스와 자식 클래스 사이의 강한 결합으로 인해 코드의 수정을 어렵게 만든다.
    • 중복 코드를 제거하기 위해 상속을 사용하였는데 새로운 로직을 추가하기 위해서는 부모, 자식 클래스 모두를 수정해야 한다.

    상속을 위한 경고 1
    자식 클래스의 메서드 안에서 super 참조를 이용해 부모 클래스의 메서드를 직접 호출할 경우 두 클래스는 강하게 결합된다.
    super 호출을 제거할 수 있는 방법을 찾아 결합도를 제거하라.

    • 상속 관계로 연결된 자식 클래스가 부모 클래스의 변경에 취약해지는 현상을 취약한 기반 클래스 문제라고 부른다.

    02 취약한 기반 클래스 문제(Fragile Base Class Problem, Brittle Base Class Problem)

    • 부모 클래스의 작은 변경에도 자식 클래스는 컴파일 오류와 실행 에러라는 고통에 시달린다.
    • 이 문제는 상속을 사용한다면 피할 수 없는 객체지향 프로그래밍의 근본적인 취약성이다.
    • 상속 관계를 추가할수록 전체 시스템의 결합도가 높아진다는 사실을 알고 있어야 한다.
    • 취약한 기반 클래스 문제는 캡슐화를 약화시키고 결합도를 높인다.
    • 캡슐화는 변경에 의한 파급효과를 제어할 수 있기 때문에 가치가 있다.
    • 객체지향의 기반은 캡슐화를 통한 변경의 통제이다.
    • 상속은 코드의 재사용을 위해 캡슐화의 장점을 희석시키고 구현에 대한 결합도를 높임으로써 객체지향이 가진 강력함을 반감시킨다.

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

    • java.util.Propertiesjava.util.Stack 예시를 통해 부모 클래스에 public 인터페이스를 불필요하게 상속받아 발생하는 문제점을 보여준다.

    상속을 위한 경고 2
    상속받은 부모 클래스의 메서드가 자식 클래스의 내부 구조에 대한 규칙을 깨트릴 수 있다.

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

    • 예시 코드를 통해 부모 클래스의 메서드를 오버라이딩 했을때 부모 클래스의 구현에 결합되어 발생할 수 있는 문제를 보여준다.

    상속을 위한 경고 3
    자식 클래스가 부모 클래스의 메서드를 오버라이딩할 경우 부모 클래스가 자신의 메서드를 사용하는 방법에 자식 클래스가 결합될 수 있다.

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

    • 자식 클래스는 부모 클래스의 메서드를 오버라이딩하거나 불필요한 인터페이스를 상속받지 않았음에도 부모 클래스를 수정할 때 자식 클래스를 함께 수정해야 할 수도 있다
    • 결합도란 다른 대상에 대해 알고 있는 지식의 양이다.
    • 상속은 기본적으로 부모 클래스의 구현을 재사용한다는 기본 전제를 따르기 때문에 자식 클래스가 부모 클래스의 내부에 대해 속속들이 알도록 강요한다.

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

    03 Phone 다시 살펴보기

    추상화에 의존하자

    • 필자의 코드 중복을 제거하기 위해 상속을 도입할 때 따르는 두 가지 원칙
      1. 두 메서드가 유사하게 보인다면 차이점을 메서드로 추출하라. 메서드 추출을 통해 두 메서드를 동일한 형태로 보이도록 만들 수 있다.
      2. 부모 클래스의 코드를 하위로 내리지 말고 자식 클래스의 코드를 상위로 올려라. 부모 클래스의 구체적인 메서드를 자식 클래스로 내리는 것보다 자식 클래스의 추상적인 메서드를 부모 클래스로 올리는 것이 재사용성과 응집도 측면에서 더 뛰어난 결과를 얻을 수 있다.

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

    • 가장 먼저 할 일은 중복 코드 안에서 차이점을 별도 메서드로 추출하는 것이다.
      이것은 흔히 말하는 "변하는 것으로부터 변하지 않는 것을 분리하라" 또는 "변하는 부분을 찾고 이를 캡슐화하라"라는 조언을 메서드 수준에서 적용한 것이다.

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

    • 부모 클래스를 추가하자.
    • 목표는 모든 클래스들이 추상화에 의존하도록 만드는 것이기 때문에 이 클래스는 추상 클래스로 구현하는 것이 적합할 것이다.
    • 클래스들의 공통(완전히 동일한) 부분을 부모 클래스로 이동시키자.
      메서드 -> 변수 순으로 옮기는 것이 편리하다.

    추상화가 핵심이다.

    • 공통 코드를 이동시킨 후에 각 클래스는 서로 다른 변경의 이유를 가진다는 것에 주목하라.
      단일 책임 원칙을 준수하게 되었다.
    • 새로운 클래스가 필요하다면 부모 클래스를 상속받는 새로운 클래스를 만들고 메서드만 오버라이딩하면 된다.
      다른 클래스들을 수정할 필요가 없다.
      현재 설계는 확장에는 열려 있고 수정에는 닫혀 있기 때문에
      개방-폐쇄 원칙을 준수하게 되었다.

    의도를 드러내는 이름 선택하기

    • 인스턴스 변수가 추가되는 경우에는 자식, 부모 클래스 모두 영향을 유발한다.

     

    위 내용들을 도입하여 최대한 상속으로 인한 결합을 피하려고 했으나 완전히 피할 수 있는 방법은 없다.
    상속은 어떤 방식으로든 부모 클래스와 자식 클래스를 결합시킨다.
    메서드 구현에 대한 결합은 추상 메서드를 추가함으로써 어느 정도 완화할 수 있지만 인스턴스 변수에 대한 잠재적인 결합을 제거할 수 있는 방법은 없다.
    우리가 원하는 것은 행동을 변경하기 위해 인스턴스 변수를 추가하더라도 상속 계층 전체에 걸쳐 부작용이 퍼지지 않게 막는 것이다.

    04 차이에 의한 프로그래밍

    • 기존 코드와 다른 부분만을 추가함으로써 애플리케이션의 기능을 확장하는 방법을 차이에 의한 프로그래밍(programming by difference)라고 부른다.
    • 차이에 의한 프로그래밍의 목표는 중복 코드를 제거하고 코드를 재사용하는 것이다.
    • 사실 중복 코드 제거와 코드 재사용은 동일한 행동을 가르키는 서로 다른 단어이다.
    • 중복을 제거하기 위해서는 코드를 재사용 가능한 단위로 분해하고 재구성해야 한다.
    • 코드를 재사용하기 위해서는 중복 코드를 제거해서 하나의 모듈로 모아야 한다.
    • 프로그래밍의 세계에서 중복 코드는 악의 근원이다.
    • 객체지향 세계에서 중복 코드를 제거하고 코드를 재사용할 수 있는 가장 유명한 방법은 상속이다.
    • 상속은 코드 재사용과 관련된 대부분의 경우에 우아한 해결 방법이 아니다. 더 좋은 방법은 합성이다.
    반응형

    댓글

Designed by Tistory.