Menu
Coddy logo textTech

합성 vs 상속

Coddy C++ 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨. 104개 중 76번째.

클래스 간의 관계를 설계할 때는 두 가지 주요 선택지가 있습니다. 상속(“is-a”)과 composition(“has-a”)입니다. 올바른 접근 방식을 선택하는 것은 코드의 유연성과 유지 관리성에 큰 영향을 미칩니다.

상속은 클래스 간에 강한 결합을 만듭니다. Vehicle을 상속하는 Car는 해당 관계에 영구적으로 묶입니다:

class Vehicle {
public:
    virtual void start() { std::cout << "Starting vehicle\n"; }
};

class Car : public Vehicle {
public:
    void start() override { std::cout << "Starting car\n"; }
};

Composition은 객체를 멤버로 포함하여 더 유연한 관계를 만듭니다. Car는 엔진인 것이 아니라 Engine을 가집니다:

class Engine {
public:
    void start() { std::cout << "Engine running\n"; }
};

class Car {
private:
    Engine engine;  // Car는 Engine을 가짐
public:
    void start() { engine.start(); }
};

composition의 핵심 장점은 유연성입니다. components를 쉽게 교체하고, 런타임에 동작을 변경하거나, 경직된 class 계층 구조의 제약 없이 여러 기능을 결합할 수 있습니다. 대부분의 경우 현대적인 C++ 설계에서는 상속보다 composition을 선호합니다.

진정한 "is-a" 관계가 있고 다형성이 필요할 때 상속을 사용하세요. 기능을 재사용하고 싶거나 관계를 "has-a" 또는 "uses-a"로 설명하는 것이 더 적절할 때는 컴포지션을 사용하세요.

challenge icon

챌린지

쉬움

상속 대신 컴포지션을 언제 사용해야 하는지 보여 주는 컴퓨터 시스템 시뮬레이터를 만들어 보겠습니다. 컴퓨터가 컴포넌트를 포함하는 것(컴포지션)을 모델링하고, 컴포넌트가 되는 것이 아님을 보여 줌으로써 컴포지션이 런타임에 부품을 교체할 수 있는 유연성을 어떻게 제공하는지 확인합니다.

코드를 세 개의 파일로 구성합니다:

  • Components.h: 컴퓨터가 contain할 수 있는 개별 컴포넌트를 정의합니다.

    private std::string modelint cores를 갖는 CPU class를 Create합니다. 두 값을 모두 받는 constructor와 CPU [model] processing with [cores] cores를 출력하는 process() method, 그리고 getModel() method를 제공합니다.

    private int sizeGB를 갖는 Memory class를 Create합니다. constructor와 Loading data into [sizeGB]GB RAM을 출력하는 load() method, 그리고 getSize() method를 제공합니다.

    private std::string type(예: "SSD" 또는 "HDD")과 int capacityGB를 갖는 Storage class를 Create합니다. constructor와 Reading from [capacityGB]GB [type]을 출력하는 read() method, 그리고 두 field에 대한 getter method를 제공합니다.

  • Computer.h: 컴포지션을 사용하여 컴포넌트를 결합하는 Computer class를 정의합니다.

    Computer는 private member object로 CPU, Memory, Storage를 가져야 합니다. constructor initialization list를 사용하여 parameter로부터 이러한 컴포넌트를 초기화합니다.

    다음 method를 Implement합니다:

    • boot(): Booting computer...를 출력한 다음, memory에서 load(), storage에서 read(), CPU에서 process()를 각각 별도의 줄에 호출합니다.
    • specs(): 다음 format으로 computer의 사양을 출력합니다:
      Computer Specs:
      - CPU: [model] ([cores] cores)
      - RAM: [size]GB
      - Storage: [capacity]GB [type]
  • main.cpp: 여섯 개의 입력을 읽습니다(각각 별도의 줄에 입력):
    1. CPU model 이름(string)
    2. CPU cores의 수(integer)
    3. GB 단위의 Memory size(integer)
    4. Storage type(string, 예: "SSD")
    5. GB 단위의 Storage capacity(integer)
    6. Command: "boot" 또는 "specs" 중 하나

    컴포넌트 object를 Create한 다음, 컴포지션을 사용하여 Computer를 Create합니다. Command에 Based하여 boot() 또는 specs()를 Call합니다.

예를 들어 입력이 Intel i7, 8, 16, SSD, 512, boot인 경우:

Booting computer...
Loading data into 16GB RAM
Reading from 512GB SSD
CPU Intel i7 processing with 8 cores

입력이 AMD Ryzen, 6, 32, HDD, 1000, specs인 경우:

Computer Specs:
- CPU: AMD Ryzen (6 cores)
- RAM: 32GB
- Storage: 1000GB HDD

Computer class가 어떤 component로부터도 상속받지 않는다는 점에 주목하세요. Computer는 컴포넌트를 contain합니다. 이 "has-a" relationship을 사용하면 서로 다른 컴포넌트 조합을 가진 Computer를 쉽게 Create할 수 있으며, setter method를 추가할 경우 나중에 컴포넌트를 교체할 수도 있습니다. 이러한 유연성이 상속에 비해 컴포지션이 갖는 핵심적인 장점입니다.

직접 해보기

#include <iostream>
#include <string>
#include "Computer.h"

using namespace std;

int main() {
    // 입력 읽기
    string cpuModel;
    getline(cin, cpuModel);  // CPU 모델 이름 (공백 포함 가능)
    
    int cpuCores;
    cin >> cpuCores;  // CPU 코어 수
    
    int memorySize;
    cin >> memorySize;  // 메모리 크기(GB)
    
    string storageType;
    cin >> storageType;  // 저장소 유형 (예: "SSD")
    
    int storageCapacity;
    cin >> storageCapacity;  // 저장소 용량(GB)
    
    string command;
    cin >> command;  // 명령: "boot" 또는 "specs"

    // TODO: 구성 요소 객체 생성 (CPU, Memory, Storage)
    
    // TODO: 컴포지션을 사용하여 Computer 객체 생성
    
    // TODO: 명령에 따라 boot() 또는 specs() 호출

    return 0;
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨

직접 연습해 보세요: 온라인 C++ 컴파일러