상속보단 조합을 선호하라 - Favor composition over inheritance

2025. 11. 11. 00:48·백엔드/아키텍처 & 패러다임 & 디자인 패턴

조합을 선호하라 - Favor composition over inheritance

is-a , has-a 관계의 차이

관계

  • 실행되는 프로그램 내에는 여러 객체들간의 상호작용으로 이루어진다.
  • 이러한 객체들간의 상호작용을 위한 방식이나 형태를 모델링하는 개념이 관계이다.
  • 관계는 다음과 같은 종류로 나뉘어져 있다.

 

is-a 관계

  • is-a는 말 그대로 'A는 B이다'일 때의 '~이다'와 같다.
  • Dog is a Animal 과 같은 관계를 가지는 것을 의미한다.
  • 코드에서는 관계를 상속으로 이를 표현한다.

 

has-a 관계

  • has-a 는 특정 객체를 다른 객체에 포함되는 관계를 의미한다.
  • Computer has a CPU, RAM and MainBoard 같은 것을 의미한다.
  • 코드에서는 관계를 멤버 변수로 이를 표현한다.

 

상속의 문제점

상속의 장점과 사용 이유

  • is-a 관계를 표현하기 위해
  • 재사용성
  • 유지보수성
  • 클래스 간의 계층 관계를 통한 구조 명확화
하지만 이는 상속을 잘 사용했을 때의 이야기이다. 다음은 상속의 문제점을 다룬다.

상속은 캡슐화를 깨뜨린다.

  • 보통 상속 관계를 사용하는 주 이유는 프로퍼티를 자식 클래스로 전하는데에 있다.
  • 하지만 이러한 프로퍼티 상속은 캡슐화를 깨뜨린다.
class Animal {
	protected int value;
}

class Dog extends Animal{
	public Dog(int value) {
		super.value = value;
	}
}
  • 위처럼 Dog 에서 Animal 클래스의 value 를 사용하는 것을 볼 수 있다.
  • 이는 OOP 의 개념인 캡슐화를 깨뜨리는 모습이다.

캡슐화를 깨뜨리면 무슨 문제가 생길까

  • 캡슐화를 통해 객체와 객체간의 의존도를 줄이는 것은 side-effect 를 줄이는 것과 같다.
class Animal {
	protected String value; // 자료형 변경
}

class Dog extends Animal{
	public Dog(int value) {
		// 컴파일 에러
		super.value = value;
	}
	
	public int getValue() {
		// 컴파일 에러
		return value;
	}
}
  • 하지만 위처럼 프로퍼티를 상속 간계에서 공유하는 것은 상위 계층의 변경이 하위 계층의 변경에 영향을 줄 수 있다.

프로퍼티는 상속하지 않고 메서드만 상속하면 되지 않나?

public class Main {
	public static void main(String[] args) {
		// 클라이언트 코드
		Animal a = new Dog();

		a.printAnimal();
	}
}

class Animal {
	// Animal 클래스가 공통으로 사용하면 좋을 공통 로직
	public void printAnimal() {
		System.out.println("Animal");
	}
}

class Dog extends Animal{
	// Dog 클래스가 Animal 클래스를 상속받아 확장한 메서드
	public void print() {
		System.out.println("Dog");
	}
}
  • 프로퍼티를 상속하지 않는다면 위처럼 Animal 의 공통 로직을 재사용성 좋게 선언하고 Animal 을 확장하는 Dog 같은 클래스에는 확장했으면 하는 메서드를 구현하는 경우가 일반적이다.
public class Main {
	public static void main(String[] args) {
		Animal a = new Dog();

		a.print();
	}
}

class Animal {
	// Animal 클래스가 공통으로 사용하면 좋을 공통 로직
	public void print() { // printAnimal -> print로 수정
		System.out.println("Animal");
	}
}
  • 하지만 Animal 개발자가 printAnimal 메서드 이름이 마음에 안들어서 print 로 수정했다고 치자.
  • 그럼 원래 Animal 계층의 print 가 호출되게끔 기대했던 위에 코드는 정상 작동할까?
  • 위처럼 원하는 결과가 아니게 된다.
  • 이유는 오버라이드로 판정되어서 이다.
  • 물론 컴파일에러로 실행 전에 판단될 수도 있지만 위처럼 실행중에 에러 로그도 없이 원하지 않는 실행이 될 수 있다.

Favor composition over inheritance

상속보다 컴포지션을 선호하라

  • 상속 없이 재사용성 그리고 유지보수를 위해 반복되는 코드를 어떻게 모듈화하면 좋을까
  • 방법으로 컴포지션을 활용하는 방법이 있다.

class Animal {
	public void print() {
		System.out.println("Animal");
	}
}

class Dog{
	private Animal animal = new Animal();

	public void printAnimal() {
		animal.print();
	}

	public void print() {
		System.out.println("Dog");
	}
}
  • 위 방식처럼 공통으로 사용하는 로직을 컴포지션을 통해 메서드를 호출하는 방식을 사용하면 된다.
  • 이러면 Animal 의 캡슐화를 깨뜨리지 않고 재사용 가능하다.

상속의 Overriding은 어떻게?


Animal a = new Dog();
a.print(); 
  • 위 처럼 다형성을 활용해 유연한 코드를 작성하고 싶을 수 있다.
  • 이는 interface 를 활용하면 된다.

interface AnimalInterface {
	void printAnimal();
}

class Dog implements AnimalInterface {
	private Animal animal = new Animal();

	@Override
	public void printAnimal() {
		animal.print();
	}

	public void print() {
		System.out.println("Dog");
	}
}

상속은 어떤 순간에 사용하면 좋을까?

  • 위의 상속의 문제점에서 봤듯이 잘못 설계를 하거나 잘못 변경을 하게되면 자잘한 컴파일 오류부터 로그로도 남기기 힘든 에러가 발생할 수 있다.
  • 이러한 문제점을 감안해서라도 상속을 사용한다면 다음과 같은 순간에서 사용하는 것이 좋다.
  • 문서화가 잘되어 있어야 한다.
  • 같은 개발자가 상속 관계에 있는 모든 클래스를 개발(개발에서 평생 한 개발자가 상속 관계에 있는 모든 클래스를 관리할 수 있을지는..)
  • 확실한 is-a 관계

is-a 관계에 대한 고찰(주관)

위에서 확실한 is-a 관계에서 상속을 사용하라 했는데 확실한 is-a 관계가 뭘까?

 

예제에서 다뤘던 Animal - Dog 처럼 개는 동물이니까 확실한 is-a 관계가 아닐까? 라는 고민을 했다.

하지만 확실한 is-a 관계는 현실세계 사물의 의미로 해석해서는 안된다.

 

현실세계의 사물과 소프트웨어의 객체간에는 분명한 차이가 있기 때문에 의미보단 프로퍼티의 결합을 최대한 회피하고 메서드의 재사용은 조합 관계로 다형성을 위해서는 interface로 대체하자.

 

“소프트웨어 세계는 현실세계의 투영하는 것이 아닌 은유다” - [객체지향의 사실과 오해]

결론 및 회고

이번 개념을 공부하는데 모호함을 느껴 생각과 평소 개발하는 순간을 회고하기도 하고 예전에 읽었던 책의 내용도 다시 회고하면서 공부할 수 있었습니다.

결론은 다음과 같이 내렸습니다.다음과 같은 순간이 아니면 상속을 피하고 컴포지션을 활용해야합니다.

  • 문서화가 잘되어 있어야 한다.
  • 같은 개발자가 상속 관계에 있는 모든 클래스를 개발(개발에서 평생 한 개발자가 상속 관계에 있는 모든 클래스를 관리할 수 있을지는..)
  • 확실한 is-a 관계

하지만 여기서 확실한 is-a 관계에서 모호하다고 판단했습니다.

어떤 순간에 확실한 is-a 관계라고 판단하는지 상속을 활용해야 한다는 의사 결정의 정확한 기준이 있는지가 모호했던 것 같습니다.

Animal-Dog 예시처럼 의미적으로 is-a 관계가 명확해보이는 개념들을 도메인에서 추출한다면 상속을 사용하고 싶지 않을까?라는 고민 때문에 모호하다고 느꼈습니다.

이 모호한 기준에 대한 결론은 다음과 같이 주관적으로 남겼습니다.

  • 현실세계의 개념을 소프트웨어의 개념으로 투영하지 말자 (객체지향의 사실과 오해를 참고)
  • 프로퍼티의 상속은 최대한 지양
  • 재사용을 위한 코드는 컴포지션 관계를 활용을 지향
  • 다형성을 위한 유연한 대체가능성 코드는 interface 활용을 지향

위 수순을 먼저 밟고 그럼에도 프로퍼티에 대한 상속을 확실히 해야한다면 위에서 언급했던 확실한 is-a 관계라 생각하고 상속을 고민하는 수순을 밟아야 한다고 결론지엇습니다.



'백엔드 > 아키텍처 & 패러다임 & 디자인 패턴' 카테고리의 다른 글

설계 원칙 , 전지적 2년차 뉴비 시점  (0) 2025.04.07
[디자인패턴] 음료시스템 예제로 배우는 Decorator Pattern  (0) 2024.10.24
[디자인 패턴] 센서 디스플레이 예제로 배우는 Observer Pattern  (4) 2024.10.12
[디자인 패턴] 오리 예제로 알아보는 Strategy Pattern  (0) 2024.10.11
자바 개발자가 AOP를 공부 해야하는 이유  (1) 2024.09.26
'백엔드/아키텍처 & 패러다임 & 디자인 패턴' 카테고리의 다른 글
  • 설계 원칙 , 전지적 2년차 뉴비 시점
  • [디자인패턴] 음료시스템 예제로 배우는 Decorator Pattern
  • [디자인 패턴] 센서 디스플레이 예제로 배우는 Observer Pattern
  • [디자인 패턴] 오리 예제로 알아보는 Strategy Pattern
천방지축 개발자
천방지축 개발자
기록용 블로그
    250x250
  • 천방지축 개발자
    KKanging
    천방지축 개발자
  • 전체
    오늘
    어제
    • 분류 전체보기 (127)
      • 백엔드 (32)
        • Spring framework (4)
        • SpringSecurity (6)
        • JPA (4)
        • DB (6)
        • Spring Cloud (2)
        • CI CD (0)
        • 아키텍처 & 패러다임 & 디자인 패턴 (6)
        • 시스템 설계 & 성능 개선 (4)
      • JAVA (10)
        • 언어 (3)
        • JVM (4)
      • cs (57)
        • 운영체제 (8)
        • 컴퓨터네트워크 (6)
        • 자료구조 (15)
        • 알고리즘 (8)
        • http (20)
      • 인공지능 (5)
        • Reinforcement Learning (5)
      • 기타 (9)
        • 백준 (2)
        • python (2)
        • 백준 장학금 (5)
      • 회고 (5)
        • 토덕-리팩터링 (2)
      • 아티클 (0)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    python
    heapq
    jpa n+1 문제
    완전이진트리
    프로세스
    엔티티 그래프
    자료구조
    Kruskal
    강화학습
    최대 힙
    멀티프로세서
    posix
    AVL트리
    JPA
    MSA
    점근적 표기법
    백준 장학금
    이분탐색이란
    연결리스트 종류
    스케줄링
    최소힙
    spring
    SpringSecurity
    백준장학금
    연결리스트
    HTTP
    JVM
    운영체제
    알고리즘
    힙트리
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.4
천방지축 개발자
상속보단 조합을 선호하라 - Favor composition over inheritance
상단으로

티스토리툴바