합성 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"로 설명하는 것이 더 적절할 때는 컴포지션을 사용하세요.
챌린지
쉬움상속 대신 컴포지션을 언제 사용해야 하는지 보여 주는 컴퓨터 시스템 시뮬레이터를 만들어 보겠습니다. 컴퓨터가 컴포넌트를 포함하는 것(컴포지션)을 모델링하고, 컴포넌트가 되는 것이 아님을 보여 줌으로써 컴포지션이 런타임에 부품을 교체할 수 있는 유연성을 어떻게 제공하는지 확인합니다.
코드를 세 개의 파일로 구성합니다:
Components.h: 컴퓨터가 contain할 수 있는 개별 컴포넌트를 정의합니다.private
std::string model과int cores를 갖는CPUclass를 Create합니다. 두 값을 모두 받는 constructor와CPU [model] processing with [cores] cores를 출력하는process()method, 그리고getModel()method를 제공합니다.private
int sizeGB를 갖는Memoryclass를 Create합니다. constructor와Loading data into [sizeGB]GB RAM을 출력하는load()method, 그리고getSize()method를 제공합니다.private
std::string type(예: "SSD" 또는 "HDD")과int capacityGB를 갖는Storageclass를 Create합니다. constructor와Reading from [capacityGB]GB [type]을 출력하는read()method, 그리고 두 field에 대한 getter method를 제공합니다.Computer.h: 컴포지션을 사용하여 컴포넌트를 결합하는Computerclass를 정의합니다.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: 여섯 개의 입력을 읽습니다(각각 별도의 줄에 입력):- CPU model 이름(string)
- CPU cores의 수(integer)
- GB 단위의 Memory size(integer)
- Storage type(string, 예: "SSD")
- GB 단위의 Storage capacity(integer)
- 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 HDDComputer 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;
}
이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.
객체 지향 프로그래밍의 모든 레슨
직접 연습해 보세요: 온라인 C++ 컴파일러