[Java] Composition

Juhye Pyoun·2023년 9월 8일

Java

목록 보기
9/9

상속(Inheritance)

상속(Inheritance)

하위 클래스가 상위 클래스의 특성을 재정의 한 것

클래스 상속을 통해 자식 클래스는 부모 클래스의 자원을 물려 받게 되며, 부모 클래스와 다른 부분만 추가하거나 재정의함으로써 기존 코드를 쉽게 확장할 수 있다.

상속 관계를 is-a 관계라고도 한다.

class Mobile {
	// ...
}

class Apple extends Mobile {
	// ...
}

상속의 단점

여기서 말하는 상속이란 클래스가 인터페이스를 구현하거나 인터페이스끼리 확장하는 인터페이스 상속이 아닌
클래스가 다른 클래스를 확장하는 구현상속을 의미한다

  1. 캡슐화를 위반한다
    - 상위 클래스가 어떻게 구현되느냐에 따라 하위 클래스의 동작에 이상이 생길 수 있다
    - 상위 클래스의 내부 구현이 달라지면 하위 클래스를 고쳐야할 수 있다

  2. 설계가 유연하지 못하다
    - 컴파일 시점에 객체의 Type이 정해진다

  3. 다중상속이 불가능하다
    -다른 클래스를 상속받고 있다면 추가적으로 상속을 받을 수 없다

 

컴포지션(Composition)

컴포지션(Composition)

기존 클래스가 새로운 클래스의 구성요소가 되는것

상속처럼 기존의 클래스를 확장(extend)하는 것이 아닌, 새로운 클래스를 생성하여 private 필드로 기존 클래스의 인스턴스를 참조하는 방식이다.

Composition은 콩글리쉬로 컴포지션, 또 다른 말로 조합이나 합성이라고 불린다.

컴포지션 관계를 has-a 관계라고도 한다.

class Car {
    Engine engine; // 필드로 Engine 클래스 변수를 갖는다(has)

    Car(Engine engine) {
        this.engine = engine; // 생성자 초기화 할때 클래스 필드의 값을 정하게 됨
    }

    void drive() {
        System.out.printf("%s엔진으로 드라이브~\n", engine.EngineType);
    }

    void breaks() {
        System.out.printf("%s엔진으로 브레이크~\n", engine.EngineType);
    }
}

class Engine {
    String EngineType; // 디젤, 가솔린, 전기

    Engine(String type) {
        EngineType = type;
    }
}
public class Main {
    public static void main(String[] args) {
        Car digelCar = new Car(new Engine("디젤"));
        digelCar.drive(); // 디젤엔진으로 드라이브~

        Car electroCar = new Car(new Engine("전기"));
        electroCar.drive(); // 전기엔진으로 드라이브~
    }
}

위 코드에서 볼 수 있듯이, 마치 new 생성자에 new 생성자를 받는 형식 new Car(new Engine("디젤")) 으로 쓰여진다.

Car 클래스가 Engine 클래스의 기능이 필요하다고 해서 무조건 상속하는 것이 아니라, 따로 클래스 인스턴스 변수에 저장하여 가져다 쓴다는 원리이다.

이 방식을 포워딩(forwarding)이라고 하며
필드의 인스턴스를 참조해 사용하는 메소드를 포워딩 메소드(forwarding method)라고 한다.

 

결론

상속보다는 컴포지션!
상속을 지양하고 컴포지션을 지향하자.
상속은 LSP 원칙에 따라 IS-A 관계가 반드시 성립할 때만 사용해야 한다.
하지만 현실적으로 명확하게 IS-A 관계를 보장할 수 없는 경우가 대부분이다.
=> 컴포지션 기법을 사용하는 것이 객체지향의 원칙을 위배하지 않으면서 유연한 설계가 가능하다.

 

[참고자료]

링크1🔗
링크2🔗
링크3🔗
링크4🔗

0개의 댓글