하나의 인터페이스, 하나의 메서드가 여러 형태로 동작할 수 있는 능력을 의미한다. 간단히 표현하면 동일한 코드가 다른 결과를 내는 것을 말한다.
용어정리 아래의 용어는 자주 사용하는 용어는 아닌 것 같다. 그래도 대략 어떤 것들이 다형성이라 부를 수 있는지 파악하기엔 나쁘지 않다.
서브타입 다형성 subtype polymorphism
extends
상속, implements
인터페이스가 서브타입 다형성에 속함임시 다형성 ad-hoc polymorphism
특정 타입에 대해 함수나 연산자가 다르게 동작하도록 정의하는 다형성으로 메서드 오버로딩overloading으로 구현된다. 컴파일 타임에 타입에 따라 적절한 함수를 선택한다.
파라메트릭 다형성 Parametric Polymorphism
타입을 매개변수화하여 타입 독립성을 갖는 코드를 작성하는 다형성. 제네릭generics을 통해 구현.
코에르션 다형성 Coercion Polymorphism
타입 변환을 통해 서로 다른 타입을 동일한 연산(단일 인터페이스)으로 처리하는 다형성. 암시적 또는 명시적 타입 캐스팅으로 구현. 유니온, 타입 단언 등으로 구현.
위 내용을 통해서 반드시 객체 지향 프로그래밍이 아니여도 다형성을 사용한다는 것을 알 수 있었다. OOP 라는 프로그래밍 패러다임을 위해서만 제시된 것이 아니라는 것인데, 그래서 프로그래밍 패러다임에 대해 더 알아보았다.
모든 프로그래밍 패러다임은 결국 코드 재사용성, 유지보수성을 위해서 제시되었다(고 생각한다). 이 때 다형성은 위 목표를 이루기 위한 코드의 유연성을 높이는데에 중요한 역할을 한다.
기존 명령형 프로그래밍*(ex.절차형 프로그래밍)에서는 모듈(module)과 구성(composition)을 통해 재사용성을 확보하는 것이 목적이었다. 시간이 지남에 따라 객체 중심의 프로그래밍 패러다임이 제시되었고 기존 명령형 프로그래밍(ex.절차형 프로그래밍)*에서 중요시하던 모듈, 구성이 제공하는 재 사용성은 물론 다형성과 상속을 더해 더 높은 코드 재사용성, 가독성, 유지보수성에 기여할 수 있는 프로그래밍 패턴이 도입된 것이다. 구성과 상속 이 대비 되는 개념인 줄 알았는데 이는 OOP에서 상속을 하도 강조하다 보니 생긴 착오였다. OOP에서는 구성도 매우 중요한 개념이며 오히려 상속을 잘 사용하는 것이 어렵기 때문에 구성을 선호하는 경우도 많다고 한다. 실제로 React와 같은 UI 프레임워크에서는 구성만을 사용하기도 한다. 현대 OOP에서는 "composition over inheritance” 라는 키워드가 제시되며 상속을 최소화하는 추세로 접어들었다고 한다.
명령형 프로그래밍*(ex.절차형 프로그래밍)은 상태를 명시적으로 직접 변경하고 코드 실행의 순서, 함수가 어떻게 실행되는지 그 과정이 중요시 된다. 즉 어떻게How에 중심을 둔 것이다. 이때, 이와 대비되는 프로그래밍 방식이 있다. 바로, 선언형 프로그래밍(ex.함수형 프로그래밍)* 프로그래밍이다. 이는 무엇을what에 중심을 둔 것이다.
선언형 프로그래밍*(ex.함수형 프로그래밍)은 상태를 직접 변경하는 명령형 프로그래밍(ex.절차형 프로그래밍)과 다르게 데이터와 로직을 분리하여 **데이터 불변성immutability***를 유지하고, 로직을 순수 함수로 구성한다. 결과적으로, 코드 예측 가능성이 높아져서 유지보수에 용이하며 부작용을 최소화하여 병렬처리 및 테스트 용이성이 높아진다.
그렇다면 객체 중심의 프로그래밍 패러다임이 제시된 이유는 무엇이었을까? 이는 프로그래밍의 복잡성이 증가함에 따라 실세계를 더 잘 반영한 모델링이 필요하게 되면서 제시되었다.
기존의 절차형 프로그래밍은 프로시저(함수)를 분리하여 순차적 명령에 의존하는 형식으로, 소프트웨어 규모가 점차 커짐에 따라 코드 관리 혼잡, 제어 혼잡, 유지보수성 감소의 이유로 대규모 프로젝트의 실패로 이어졌다. 이 시기, 대규모 프로젝트를 관리하고자 프로그램을 **데이터(상태)와 데이터를 다루는 행동(메서드, 로직)**을 분리하고 동시에 객체 단위로 묶어 캡슐화함으로써 높은 응집도와 낮은 결합도를 유지하도록 하는 객체 중심의 프로그래밍 패러다임이 제시된 것이다. 이는 생물학적 세포나 네트워크 시스템과 같이 독립적이면서도 메시지 전달message passing 로 상호작용하는 구조를 모방한 것으로 이를 통해 절차형 프로그래밍의 한계를 극복하고자 하였다. 작은 규모의 프로젝트에서도 추후 확장성을 고려하여 유지보수가 가능하게끔 미리 설계할 수 있는 방법을 제시한 것이다. 이러한 방식의 프로그래밍은 기능 변경 시, 변경의 범위가 작아 변경 시 파급 효과도 예측가능하다. 그러니 테스트를 하기에도 용이하다.