복잡한뇌구조마냥

[JAVA] 구성 ( Composition ) 본문

BE/JAVA

[JAVA] 구성 ( Composition )

지금해냥 2025. 7. 29. 10:02

✅ 개요

Composition(구성) 은 객체지향 프로그래밍에서 객체 간 관계를 형성하는 중요한 설계 기법입니다.
보통 자바에서 객체 간 재사용을 구현할 때 흔히 상속(inheritance)을 떠올리지만, Composition은 상속보다 더 유연하고 유지보수가 쉬운 설계 방식으로 권장되는 경우가 많습니다.


🔍 1. Composition이란?

Composition이란 다른 객체를 자신의 필드로 포함하여 기능을 위임(Delegate)하는 방식입니다.
즉, "has-a" 관계를 나타냅니다.

class Engine {
    public void start() {
        System.out.println("Engine starting...");
    }
}

class Car {
    private Engine engine = new Engine(); // Car has-a Engine

    public void drive() {
        engine.start(); // 기능 위임
        System.out.println("Car is driving...");
    }
}

📌 Car는 Engine을 상속받지 않았지만, 내부에 포함(Composition)시켜 기능을 사용하고 있습니다.


2. 상속(Inheritance) vs 구성(Composition) 🆚

 

항목 상속 (Inheritance) 구성 (Composition)
관계 "is-a" 관계 "has-a" 관계
유연성 낮음 (강한 결합) 높음 (약한 결합)
재사용성 부모 클래스에 종속적 독립적 재사용 가능
변경에 대한 유연성 취약 견고
예시 Bird extends Animal Bird has-a Wing
 

📌 Composition은 실행 중(runtime)에 객체를 바꿀 수 있어 동적으로 행동을 바꾸기 쉽습니다.


🚫 3. 상속의 한계

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

class FaxPrinter extends Printer {
    public void fax() {
        System.out.println("Faxing...");
    }
}

이 방식은 언뜻 보면 괜찮아 보이지만...

  • FaxPrinter는 Printer의 모든 기능에 종속됩니다.
  • 만약 Printer의 기능이 바뀌면, FaxPrinter도 영향을 받습니다.
  • 코드 수정 시 영향 범위가 넓습니다.

✅ 4. Composition을 활용한 리팩토링

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

class Fax {
    public void fax() {
        System.out.println("Faxing...");
    }
}

class FaxPrinter {
    private Printer printer;
    private Fax fax;

    public FaxPrinter() {
        this.printer = new Printer();
        this.fax = new Fax();
    }

    public void print() {
        printer.print();
    }

    public void fax() {
        fax.fax();
    }
}

이제 FaxPrinter는 필요한 기능만 조합하여 사용할 수 있으며,
Printer나 Fax의 변경이 있어도 영향을 최소화할 수 있습니다.


🎯 5. Composition의 장점 요약

  • 유연성: 기능을 조합해서 동적으로 행동을 바꿀 수 있음
  • 재사용성: 여러 클래스에서 같은 객체 조합 가능
  • 의존성 분리: 각 컴포넌트는 독립적으로 테스트 가능
  • SOLID 원칙 중 DIP/ISP 만족: 상위 모듈에 의존하지 않음

🧠 실무 팁

  • 상속은 정말 “is-a” 관계일 때만 사용하세요.
  • 구현보다 인터페이스를 활용한 Composition이 더욱 유연합니다.
  • 다양한 라이브러리나 프레임워크에서도 Composition 기반 설계가 일반적입니다 (ex. Spring DI).

📝 마무리

상속은 너무 강한 관계입니다.
변화에 강하고 테스트 가능한 코드를 짜기 위해선 Composition 중심의 객체 설계를 고려해보세요.

“상속보다는 조합을 사용하라 (Favor Composition Over Inheritance)”
— Effective Java

LIST