Java

상속과 컴포지션

lala9663 2023. 10. 4. 18:38

이펙티브 자바책에 상속보다는 컴포지션을 사용해라 라는 주제가 있다.


이 주제를 보며 어? 그런가? 라는 생각이 들어 정리해 보았다. ㅋㅋㅋ,,,

상속?

상속은 is-a 관계를 나타낸다. 예를 들면, 자동차가 차량 클래스로부터 상속받는 경우, 자동차는 차량이라고 할 수 있다.

장점

  1. 코드 재사용: 부모 클래스의 기능을 자식 클래스가 상속받기 때문에, 중복된 코드를 작성할 필요가 없어 코드 재사용성이 높아진다.
  2. 확장성: 자식 클래스는 부모 클래스의 특성을 확장하거나 변경할 수 있다. 새로운 메서드를 추가하거나 기존 메서드를 재정의하여 새로운 동작을 추가할 수 있다.
  3. 폴리모피즘(Polymorphism) 구현: 상속을 통해 여러 클래스가 동일한 인터페이스를 공유하고, 이를 통해 다형성을 구현할 수 있다.(객체지향 프로그래밍의 강력한 기능 중 하나)
  4. 결합도 증가: 자식 클래스는 부모 클래스에 의존하므로, 부모 클래스의 변경이 자식 클래스에 영향을 미칠 수 있다. 이는 결합도를 높이고 유지보수를 어렵게 만들 수 있다.
  5. 복잡성 증가: 상속 계층이 깊어질수록 코드의 복잡성이 증가할 수 있으며, 클래스 간의 관계를 이해하기 어려워질 수 있다.
  6. 제한된 확장성: 이미 다른 클래스를 상속받은 경우, 더 이상 다른 클래스의 상속받을 수 없으므로 확장성이 제한될 수 있다. ([[Item 19]])
  7. 강한 결합도: 상속은 부모 클래스와 자식 클래스 간에 강한 결합을 만들 수 있으므로, 이로 인해 재사용성과 유지보수성이 저하될 수 있다.

 

컴포지션?

객체지향 프로그래밍에서 한 클래스가 다른 클래스의 인스턴스를 포함하고, 이를 활용하여 원하는 기능을 구현하는 개념이다. 즉, 컴포지션은 객체들을 조합하여 더 복잡한 동작을 수행하기 위한 방법 중 하나이다.

컴포지션은 주로 has-a 관계를 나타낸다. 예를 들어, 자동차 클래스가 엔진 클래스의 인스턴스를 포함하는 경우, 자동차 "has-an" 엔진이다. 이를 코드로 나타내면

public class Engine {
    // 엔진 관련 기능들
}

public class Car {
    private Engine engine; // 자동차는 엔진을 포함

    public Car() {
        engine = new Engine(); // 컴포지션을 통해 엔진을 생성 및 포함
    }

    // 자동차와 관련된 기능들
}

위의 예에서 자동차 클래스는 엔진 클래스의 인스턴스를 포함하고 있으며, 이를 통해 자동차의 동작을 구현한다. 이것이 컴포지션의 핵심이다.

 

장점

  1. 유연성: 컴포지션을 사용하면 더 작은 단위로 객체를 조립할 수 있으므로, 필요한 동작을 더 쉽게 변경하거나 확장할 수 있다. 이는 객체들 간의 느슨한 결합을 유지하며 변경이 쉬운 코드를 작성하는 데 도움이 된다.
  2. 코드 재사용: 컴포지션을 통해 더 작은 컴포넌트를 재사용하여 새로운 기능을 구현할 수 있다. 기존의 클래스를 상속받는 것보다 더 모듈화 된 컴포넌트를 활용할 수 있다.
  3. 결합도 감소: 컴포지션은 클래스 간의 결합도를 낮추고, 하나의 클래스가 다른 클래스의 내부 구현에 의존하지 않도록 한다. 이로써 변경 시 다른 클래스에 미치는 영향을 최소화할 수 있다.

그럼 각각 언제 사용하는 게 좋을까?

상속을 사용하는 경우

  1. is-a 관계: 클래스 A가 클래스 B의 특수한 경우로 설명될 때 상속을 고려한다. 예를 들어, "사람은 동물이다"라고 말할 수 있으며, 이 경우 "Person"클래스가 "Animal"클래스를 상속받는 것은 합리적이다.
  2. 부모 클래스의 기능 확장: 부모 클래스의 기능을 확장하고자 할 대 상속이 유용하다. 자식 클래스는 부모 클래스의 메서드를 재정의하여 새로운 동작을 추가하거나 변경할 수 있다.
  3. 다형성 활용: 다형성을 이용하여 여러 클래스를 동일한 인터페이스로 사용하려면, 상속을 통해 공통된 부모 클래스를 만들 수 있다.

컴포지션을 사용하는 경우

  1. has-a 관계: 클래스 A가 클래스 B를 포함하고 있지만 클래스 B가 클래스 A의 일부가 아닌 경우 컴포지션을 고려한다. 예를 들어, "자동차는 엔진을 가지고 있다"라고 말할 때, "Car"클래스가 "Engine"클래스의 인스턴스를 포함하면 컴포지션을 사용한다.
  2. 유연한 설계: 유연성과 느슨한 결합을 원할 때 컴포지션을 사용한다.
  3. 다중 상속 회피: 다중 상속 문제를 피하려면 컴포지션을 활용할 수 있다.
  4. 인터페이스 분리 원칙: 컴포지션은 인터페이스 분리 원칙(ISP)을 지키기 쉽게 만든다. 필요한 기능을 가진 작은 컴포넌트를 조합하여 인터페이스를 더 작고 명확하게 유지할 수 있다.