이전글: Symbol#to_proc 전체보기
루비의 클래스는 항상 열려 있습니다. 그래서 이미 정의된 클래스에 얼마든지 메서드를 추가할 수 있습니다. 내가 정의한 클래스가 아니라 내장 클래스, 심지어는 Object, Kernel, Module 등에도 메서드를 마음껏 추가할 수 있습니다. 다른 언어, 특히 정적 언어와 구분되는 루비의 특징입니다. 클래스를 점진적으로 완성해갈 수 있으니, 곡괭이 책을 쓸 때도 무척이나 도움이 되었다고 하는군요.
이 점을 활용하면, 언어의 표현력이 매우 높아집니다. 생각할 수 있는 대부분을 표현할 수 있게 되지요. 루비가 도메인 언어를 구현하기에 최적의 언어라고 하는 데는 열린 클래스의 역할도 큽니다.
이를 잘 활용한 예를 액티브 서포트에서 찾을 수 있습니다. 다음 코드를 보세요.
위와 같은 코드를 작성할 수 있는 이유는 Numeric 클래스(루비에서 숫자를 표현하는 기본 클래스)를 확장할 수 있기 때문입니다. 이런 기능이 없는 언어였다면 이렇게 써야 합니다.
프리미티브 타입이라는 영역이 있고, 이를 확장할 방법이 없기 때문이지요. 위의 코드가 읽기도 쉽고, 보기도 좋습니다. 그렇지요?
구현의 측면을 한번 살펴보겠습니다. 45.bytes라는 문법이 성립하려면 45의 클래스(Numeric이나 Fixnum)에 bytes라는 메서드를 정의하면 됩니다. 루비 클래스는 열려있기 때문에 다음과 같이 쓰면 됩니다.
루비에서는 이렇게 간편한 일이지만 클래스가 닫혀있는 언어라면 어떨까요? Numeric 클래스를 상속받아서 Bytes, Kilobytes 클래스를 모두 구현해주어야 할지도 모릅니다. 물론, 코드가 불필요하게 복잡해질 것이 분명하므로 그렇게 하지 않을 것입니다. 언어의 한계로 덜 자연스러운 표현을 쓰게 되는 것이지요.
열린 클래스가 장점만 있는 것은 아닙니다. 명확한 이름 공간이 없기 때문에, 다른 사람이 확장해버린 기능 때문에, 내가 만든 모듈이 오동작을 하는 등 충돌이 있을 수도 있습니다. 무분별한 확장은 오히려 코드를 따라가기 어려울 정도로 어렵게 만들기도 합니다. 하지만, 적당한 사용, 특히나 자연스럽게 DSL을 만들어 과정은 즐거운 경험이 분명합니다.
마지막으로 루비에서 DSL을 디자인하는 과정을 소개합니다. 먼저 루비를 잊고, 자신이 느끼기에 해당 도메인을 가장 잘 표현할 수 있는 언어로 프로그램을 작성해봅니다. 예를 들어 영화 예매 언어라고 해봅시다. 내가 특정 조건을 입력하면 가장 적합한 영화를 추천해주고, 또 예매까지 해줍니다.
저렇게 자연어로 입력해도 컴퓨터가 알아서 처리해주면 루비도 필요 없겠지요? 이 조건을 루비 문법으로 옮겨봅니다.
우리가 만들 DSL의 모양이 나왔습니다. 이제 한 줄씩 구현 해주면 됩니다. 테스트 주도 개발을 해가면 어렵지 않게 원하는 DSL을 손에 넣을 수 있습니다. 조금 억지스러운 예제라 구현은 생략합니다.
- 강문식