포스트

[OOP] 상속성(Inheritance)

객체지향 프로그래밍(OOP)의 상속성(Inheritance)은 기존 클래스의 특성과 동작을 새로운 클래스가 물려받아 재사용하고 확장할 수 있는 특징을 말합니다.

이때, 기존 클래스를 ‘기본 클래스’ 혹은 ‘부모 클래스’라 하고, 물려받은 클래스를 ‘파생 클래스’ 혹은 ‘자식 클래스’라고 부릅니다.

절차적 프로그래밍의 한계

객체지향 프로그래밍은 기존 절차적 프로그래밍의 한계를 극복하기 위해 만들어진 프로그래밍 패러다임입니다.

절차적 프로그래밍에서는 주로 함수와 데이터를 분리하여 코드를 작성하는 방식이 사용되었습니다.

하지만 소프트웨어가 점점 더 복잡해지고 규모가 커지면서, 코드와 의존 관계가 복잡해지고 유지보수와 확장이 어려워지는 문제점을 드러내기 시작했습니다.

이러한 문제점을 해결하기 위해 객체 지향 프로그래밍이 등장하게 되었고, 그 중심 개념 중 하나가 상속성(Inheritance) 입니다.

상속성의 특징

  1. 코드 재사용성: 상속을 통해 기존 클래스의 속성메서드를 그대로 사용할 수 있어 코드 중복을 줄이고, 기존 클래스를 확장하여 새로운 기능을 추가할 수도 있습니다.
  2. 유지보수의 용이성: 공통 기능을 상위 클래스에 정의하고, 이를 상속받는 하위 클래스에서 사용함으로써 유지보수를 용이하게 만들 수 있습니다.
    • 예를 들어, 상위 클래스의 기능을 수정하면 이를 상속받는 모든 하위 클래스에 자동으로 반영되므로, 일관성 있는 수정이 가능합니다.
  3. 계층 구조: 상속성을 통해 클래스 간의 계층 구조를 명확히 할 수 있습니다. 이를 통해 시스템의 구조를 체계적으로 설계하고, 객체 간의 관계를 명확히 이해할 수 있습니다.
  4. 다형성(Polymorphism): 상속받은 클래스는 부모 클래스와 동일한 인터페이스를 통해 서로 다른 구현을 사용할 수 있게 합니다. 이는 코드를 보다 유연하고 확장 가능하게 만들어 줍니다.

상속 관계에서 생성자

제가 배운 다양한 언어에서 상속을 하는 방법(코드)은 달라도 상속 관계에서 생성자 호출 순서와 초기화 순서는 대부분 비슷했습니다.

우선, 상속 관계를 가진 클래스의 자식 클래스의 기본 생성자를 호출하면, 초기화 초기 단계에서 부모 클래스의 기본 생성자가 호출됩니다.

이는 클래스 상속 시 부모 클래스가 먼저 초기화되어야 자식 클래스가 안정적으로 초기화될 수 있기 때문입니다.

  • 생성자 호출 순서: 자식 클래스 → 부모 클래스
  • 초기화 진행 순서: 부모 클래스 → 자식 클래스

오버라이딩(Overriding)

오버라이딩은 자식 클래스가 부모 클래스에서 상속받은 메서드를 재정의하는 것을 의미합니다.
이를 통해 자식 클래스는 부모 클래스의 메서드를 자신의 필요에 맞게 수정하여 사용할 수 있습니다.

오버라이딩은 다형성(Polymorphism)의 핵심 요소이며, 코드의 유연성재사용성을 높이는 데 중요한 역할을 합니다.

오버라이딩의 특징

  1. 상속 관계: 오버라이딩은 상속 관계에 있는 클래스 간에 이루어집니다. 자식 클래스가 부모 클래스의 메서드를 재정의할 수 있습니다.
  2. 가상 메서드: 부모 클래스의 메서드는 가상 메서드(virtual method) 여야 합니다. 가상 메서드는 런타임호출할 메서드를 결정하는 데 사용됩니다.
  3. 메서드 시그니처: 오버라이딩하는 메서드는 부모 클래스의 메서드와 동일한 이름, 반환 타입, 매개변수를 가져야 합니다. 메서드 시그니처가 일치해야 합니다.
  4. 접근 제한자: 자식 클래스에서 오버라이딩한 메서드는 부모 클래스의 메서드와 동일하거나 더 넓은 접근 제한자를 가져야 합니다.
    예를 들어, 부모 클래스의 메서드가 protected라면, 자식 클래스의 메서드는 protected나 public이어야 합니다.

접근 제한자에서 어쩌면 “자식 클래스에서는 접근 제한자를 더 강화해야 하는거 아닌가?”라고 생각 하실 수도 있겠지만, 이는 리스코프 치환 원칙과 관련이 있습니다.

다형성과 관련하여 상속과 관련된 중요한 원칙이므로 한 번씩 읽어 보시는 것을 추천드립니다.

상속성의 단점

상속성은 객체 지향 프로그래밍에서 코드 재사용성과 계층 구조의 명확화를 제공하는 강력한 도구입니다.

하지만 상속을 사용할 때는 몇 가지 단점과 주의해야 할 점들이 있습니다.

  1. 상위 클래스 변경의 어려움
    • 상속 구조에서 상위 클래스를 변경하면 그 변경 사항이 모든 하위 클래스에 영향을 미칩니다. 이는 하위 클래스에 예상치 못한 부작용을 일으킬 수 있으며, 시스템의 다른 부분까지 영향을 미칠 수 있습니다.
    • 따라서 상위 클래스는 매우 신중하게 관리되어야 합니다.
  2. 클래스의 양 증가
    • 상속을 사용하면 필요 이상으로 많은 클래스가 생성될 수 있습니다. 각 클래스는 특정 기능을 수행하지만, 상속 계층이 복잡해질수록 시스템을 이해하고 관리하기 어려워집니다.
  3. 상속의 오용
    • 상속은 간혹 잘못 사용되기도 합니다. 예를 들어, 상위 클래스의 기능과 맞지 않는 새로운 메소드를 추가하거나, 상위 클래스의 메소드를 잘못 확장함으로써 기능의 의도를 흐리게 만들 수 있습니다. 이는 코드의 읽기 어려움과 유지 관리의 복잡성을 증가시킵니다.

상속 하기 전에 생각 해보기

  1. 진정한 하위 타입인 경우에만 상속 사용
    • 상속을 사용할 때는 “is-a” 관계가 명확히 성립되는지 확인해야 합니다.
    • 예를 들어, ‘개’는 ‘동물’의 하위 타입이므로 상속이 적절하지만, ‘사용자 인터페이스’를 ‘사용자’에게 상속시키는 것은 적절하지 않습니다.
  2. 컴포넌트 사용 (has-a 관계)
    • 상속 대신 컴포넌트를 사용할 수 있습니다. 이는 객체가 다른 객체를 포함하는 구조로, 각 객체의 기능을 유연하게 확장할 수 있게 해줍니다.
    • 이 방법은 단일 책임 원칙을 준수할 수 있게 도와주며, 코드의 재사용성을 높이면서도 계층 구조의 복잡성을 줄일 수 있습니다.

참고

[OOP] 객체지향 프로그래밍(Object-Oriented Programming, OOP) 정리

[OOP] 캡슐화 (Encapsulation)

[OOP] 추상화(Abstraction)

[OOP] 다형성(Polymorphism)

이 기사는 저작권자의 CC BY-NC-ND 4.0 라이센스를 따릅니다.