다형성(Polymorphism)이란 무엇인가?
다형성(Polymorphism)은 그리스어에서 유래한 용어입니다. '많은'이라는 의미의 Poly와 '형태'라는 의미의 morphism이 결합된 단어로, 말 그대로 '여러 가지 형태'를 뜻합니다.
객체 지향 프로그래밍(OOP)에서 다형성은 서로 다른 클래스에 속한 메서드들이 유사한 작업을 수행할 때 동일한 이름을 사용해야 한다는 원칙을 설명하는 디자인 패턴입니다. 다형성을 활용하면 기능이 서로 다른 여러 클래스가 하나의 공통 인터페이스(common interface)를 공유하거나 실행할 수 있게 됩니다.
다형성의 가장 큰 장점은 코드 사용 방식의 일관성입니다. 해당 코드가 어느 클래스에 속해 있는지와 관계없이, 호출하는 쪽에서는 항상 같은 방식으로 메서드를 사용할 수 있습니다. 덕분에 코드의 가독성과 유지보수성이 크게 향상됩니다.
클래스가 다형성 원칙을 확실하게 따르도록 강제하려면 두 가지 방법 중 하나를 선택할 수 있습니다. 바로 추상 클래스(abstract class)와 인터페이스(interface)입니다.
인터페이스(Interface)란?
인터페이스는 클래스와 형태가 비슷하지만, 실제 실행 코드(구현 내용)를 포함할 수 없다는 점이 다릅니다. 인터페이스는 메서드의 이름과 매개변수만 정의할 수 있으며, 메서드의 본문은 작성할 수 없습니다.
그리고 인터페이스를 구현(implements)하는 클래스는 반드시 인터페이스에 선언된 모든 메서드를 구현해야 하는 규약을 지니게 됩니다.
예제 코드
아래는 인터페이스를 활용해 다형성 원칙을 구현한 PHP 예제입니다.
<?php
interface Machine {
public function calcTask();
}
class Circle implements Machine {
private $radius;
public function __construct($radius){
$this->radius = $radius;
}
public function calcTask(){
return round($this->radius * $this->radius * pi(), 3);
}
}
class Rectangle implements Machine {
private $width;
private $height;
public function __construct($width, $height){
$this->width = $width;
$this->height = $height;
}
public function calcTask(){
return $this->width * $this->height;
}
}
$mycirc = new Circle(3);
$myrect = new Rectangle(3, 4);
echo $mycirc->calcTask();
echo "<br>";
echo $myrect->calcTask();
?>
실행 결과
28.274
12
코드 설명
위 예제에서 'Machine'이라는 이름의 인터페이스는 이를 구현하는 모든 클래스에게 calcTask()라는 이름의 추상 메서드를 반드시 정의하도록 요구합니다.
Circle 클래스는 Machine 인터페이스를 구현하면서, calcTask() 메서드 안에 원의 넓이를 계산하는 로직(반지름 × 반지름 × π)을 작성했습니다. Rectangle 클래스 역시 Machine 인터페이스를 구현하지만, calcTask() 메서드에는 사각형의 넓이를 계산하는 로직(가로 × 세로)을 담고 있어 Circle 클래스와는 전혀 다른 내용을 수행합니다.
다형성 원칙에 따라, 작업을 계산하는 모든 메서드는 동일한 이름(calcTask)을 사용합니다. 따라서 서로 다른 도형의 넓이를 구하고 싶을 때 우리는 메서드 이름만 알면 됩니다. 각 클래스 내부에서 실제로 어떤 수학적 연산이 일어나는지는 전혀 신경 쓸 필요가 없습니다. 필요하다면 언제든 내부 계산 로직을 수정하거나 새로운 도형 클래스를 추가할 수 있으며, 호출부 코드는 그대로 유지됩니다.
다형성의 핵심 장점 정리
- 일관성: 동일한 메서드 이름으로 여러 클래스를 다룰 수 있어 코드가 직관적입니다.
- 확장성: 새로운 클래스를 추가하더라도 기존 코드를 수정할 필요가 거의 없습니다.
- 유지보수성: 내부 구현을 변경해도 인터페이스만 지켜지면 호출부에 영향이 없습니다.