지난 글에서 밝혔던 바와 같이 이번 시간에는 polymorphism에 대해서 알아보려고 한다.
Polymorphism
다형성이라고 하며, 하난의 메시지에 대해 서로 다른 객체가 서로 다른 방식으로 응답할 수 있는 기능이다.
메시지를 보내는 객체는 어느 객체가 받을지 알 필요가 없다.
- one interface, multiple implementations
- 연산 오버라이딩(상속) + 동적 바인딩 을 통해 구현된다.
클래스 차원의 encapsulation(캡슐화)를 구현한 구조이다.
- superclass가 subclass들을 encapsulation 한다.
- 메시지를 보내는 객체는 그 component 중에서 superclass의 interface만 알고 있으면 된다.
- 내부의 상속 구조에 대해 전혀 알 필요가 없다. (누가 누굴 상속하고 있는지도 알 필요 없음)
장점
- 다른 component의 재사용(reuse)에 용이하다.
- component 단위로 모듈화(module)되어 독립적으로 구현되므로 서로 간의 의존성이 감소한다.
- 코드의 quality 향상과 maintanance에 유리하다.
아래는 Polymorphism의 예시이다.
TaxiDriver + TransportationCompany
class TaxiDriver
{
private:
char driverName[10];
int driverAge;
int baseSalary;
int bonusMoney;
public:
TaxiDriver(const char* driverName, int driverAge, int baseSalary, int bonusMoney);
char* getDriverName() const;
int getDriverAge() const;
int getSalary() const;
void showDriverInfo() const;
};
class TransportationComany
{
private:
TaxiDriver* driverList[50];
int numDrivers;
public:
TransportationComany() : numDrivers(0) {}
void addDriver(TaxiDriver* driver)
{
driverList[numDrivers++] = driver;
}
void showAllDriversInfo() const
{
for (int i = 0; i < numDrivers; i++)
driverList[i]->showDriverInfo();
}
void showTotalSalary() const
{
int sum = 0;
for (int i = 0; i < numDrivers; i++)
sum += driverList[i]->getSalary();
cout << "total salary: " << sum << endl;
}
~TransportationComany()
{
for (int i = 0; i < numDrivers; i++)
delete driverList[i];
}
};
BusDriver
class BusDriver
{
private:
char driverName[10];
int driverAge;
int workingHours;
int payPerHour;
public:
BusDriver(const char* driverName, int driverAge, int workingHours, int payPerHour);
char* getDriverName() const;
int getDriverAge() const;
int getSalary() const;
void showDriverInfo() const;
};
아래와 같은 구조로 코드가 이루어진 것을 알 수 있다.

아래는 상속 구조를 나타내는 코드이다.
class Driver
{
private:
char driverName[10];
int driverAge;
public:
Driver(const char* driverName, int driverAge);
char* getDriverName() const;
int getDriverAge() const;
};
class TaxiDriver : public Driver
{
private:
int baseSalary;
int bonusMoney;
public:
TaxiDriver(const char* driverName, int driverAge, int baseSalary, int bonusMoney);
int getSalary() const;
void showDriverInfo() const;
};
class BusDriver : public Driver
{
private:
int workingHours;
int payPerHour;
public:
BusDriver(const char* driverName, int driverAge, int workingHours, int payPerHour);
int getSalary() const;
void showDriverInfo() const;
};
아래의 코드를 확인해보자.
void DriverManager::addDrivers()
{
TransportationComany transportationComany;
TaxiDriver* pNewTaxiDriver = NULL;
BusDriver* pNewBusDriver = NULL;
pNewTaxiDriver = new TaxiDriver("Kim", 33, 300, 50);
transportationComany.addTaxiDriver(pNewTaxiDriver);
pNewTaxiDriver = new TaxiDriver("Lee", 45, 350, 60);
transportationComany.addTaxiDriver(pNewTaxiDriver);
pNewBusDriver = new BusDriver("Park", 36, 40, 5);
transportationComany.addBusDriver(pNewBusDriver);
pNewBusDriver = new BusDriver("Yoo", 40, 30, 6);
transportationComany.addBusDriver(pNewBusDriver);
transportationComany.showAllDriversInfo();
transportationComany.showTotalSalary();
}
- 코드 의존성 존재
- 외부 코드(TransportationCompany)에서 inheritance에 참가한 모든 subclass의 구조를 알아야 사용 가능하다.
- 새로운 subclass 생성 혹은 삭제 시, TransportationCompany class도 수정해야 한다.
- 모듈화 구조 설계
- 요구사항이 변경되어 inheritance에 참가한 class들이 수정되더라도 외부 코드는 영향을 받지 않도록 수정이 필요하다.
- 외부 코드에서는 모든 객체를 Driver class로 취급하도록 수정한다.
- 요구사항이 변경되어 inheritance에 참가한 class들이 수정되더라도 외부 코드는 영향을 받지 않도록 수정이 필요하다.
→ Polymorphism 구조 필요
Polymorphism 구조
- Operation overriding + dynamic binding 으로 구현된다.
- operating overriding
- Subclass가 superclass 함수의 prototype과 동일한 함수를 재정의하는 것이다.
- dynamic binding
- runtime 시 포인터 변수가 실제 가리키는 객체 타입을 기준으로 호출할 함수를 결정한다.
- → 'virtual' 키워드 사용
- runtime 시 포인터 변수가 실제 가리키는 객체 타입을 기준으로 호출할 함수를 결정한다.
- operating overriding
polymorphism 구조를 따른다면 다이어그램은 아래와 같이 변하게 된다.

외부 코드(TransportationCompany class)에 영향이 없고, 유지보수 및 재사용에 용이하도록 코드를 수정해보면 아래와 같다.
void DriverManager::addDrivers()
{
TransportationComany transportationComany;
Driver* pNewDriver = NULL;
pNewDriver = new TaxiDriver("Kim", 33, 300, 50);
transportationComany.addDriver(pNewDriver);
pNewDriver = new TaxiDriver("Lee", 45, 350, 60);
transportationComany.addDriver(pNewDriver);
pNewDriver = new BusDriver("Park", 36, 40, 5);
transportationComany.addDriver(pNewDriver);
pNewDriver = new BusDriver("Yoo", 40, 30, 6);
transportationComany.addDriver(pNewDriver);
transportationComany.showAllDriversInfo();
transportationComany.showTotalSalary();
}
이런식으로 polymorphism 구조를 이용한다면 TruckDriver 클래스를 추가 정의 하는 것에 큰 어려움이 없을 것이다.
class TruckDriver
{
private:
char driverName[10];
int driverAge;
int workingHours;
int truckWeight;
public:
TruckDriver(const char* driverName, int driverAge, int workingHours, int truckWeight);
char* getDriverName() const;
int getDriverAge() const;
int getSalary() const;
void showDriverInfo() const;
};
최종 polymorphism 구조는 아래와 같다.

Polymorphism 의 두번째 예시를 통해서 다형성에 대해서 더 알아보자.
Shape 예제이다.
기본 구조에서는 Rectangle, Circle, Ellipse 각각이 독립적으로 존재한다.
class Rectangle { void draw(); };
class Circle { void draw(); };
class Ellipse { void draw(); };
이 구조의 문제는 이전 Driver 예제와 동일하다.
각 타입을 따로 관리해야하며, 새로운 도형이 추가될 때마다 코드 수정이 불가피하다.
이를 해결하기 위하여 공통 부모 클래스 Shape를 정의한다.
class Shape {
public:
virtual void draw() = 0;
};
그리고 각 도형은 이를 상속받아 구현한다.
class Rectangle : public Shape {
public:
virtual void draw();
};
class Circle : public Shape {
public:
virtual void draw();
};
class Ellipse : public Shape {
public:
virtual void draw();
};
핵심은 draw() 함수가 virtual 함수라는 점이다.
이를 통해서 동적 바인딩이 발생하는 것을 알 수 있다.
Shape* shapes[5];
shapes[0] = new Circle();
shapes[1] = new Ellipse();
shapes[2] = new Rectangle();
for (int i = 0; i < num_shapes; i++)
shapes[i]->draw();
- 배열 타입은 Shape*
- 실제 객체는 Circle, Rectangle, Ellipse 이다.
하나의 인터페이스로 다양한 객체를 처리할 수 있다.
새로운 도형.. 예를 들어 Triangle이 추가되더라도, 기존 코드 수정 없이 확장이 가능하다.
polymorphism에 대해서 예시와 함께 알아보았다.
개발을 할 때 그냥 무작정 코드를 써내려 갔었는데, 다형성을 지키면서 개발을 했더라면 개발 속도도 줄고, 재사용이 쉬웠을 것 같다는 아쉬움이 생각나는 개념이었다.
다음 글에서는 객체지향의 장점과 추상클래스/인터페이스 에 대해서 정리해보고자 한다.
'CS > Software Engineering' 카테고리의 다른 글
| [소프트웨어공학/Software Engineering] 형상 관리(CM: Configuration Management)란? (0) | 2026.04.15 |
|---|---|
| [소프트웨어공학/Software Engineering] 객체지향과 인터페이스 (0) | 2026.04.10 |
| [소프트웨어공학/Software Engineering] 객체 지향이란? (2) | 2026.04.09 |
| [소프트웨어공학/Software Engineering] 애자일 소프트웨어 개발 (0) | 2026.04.07 |
| [소프트웨어공학/Software Engineering] 소프트웨어 프로세스 (2) (2) | 2026.04.03 |