대부분의 객체지향 언어들은 물론이요, 객체지향에 관한 교육역시 가장 기본이 되는 요소로 Class를 채택하고 있다고 생각한다.
하지만 나는 객체지향의 가장 중심 요소로 자리잡아야 하는 것은 Interface라고 생각한다. 이문제는 나중에 다루기로 하고…
하나의 Class를 디자인 할 때 우리는 묵시적으로 Interface도 같이 디자인하게 되는데, 그 Class가 가진 모든 public static final field나 public 메소드들의 집합은 명백한 Interface이다. 즉 모든 Class들은 자기자신에 대한 Interface를 하나씩은 가지고 있다는 이야기 이다. 이러한 명백한 진실에도 불구하고, 이러한 묵시적인 Interface를 활용할수 있는 방법이 없다. 지원하는 언어가 없기 때문이다.
(솔직히 말하자면 이 명백한 인터페이스를 왜, 묵시적 인터페이스로 불러야 하는가 부터 짜증나지만, 나보다 훨씬 잘 나신 마틴 파울러씨께서 Implict Interface라고 불러 버렸으므로 나로서는 얄짤 없다.)
예를 들어, 자바가 제공하는 Vector는 매우 훌륭하기는 하지만, Vector는 사용되는 패턴에 따라, 성능 차이가 심하기 때문에, 패턴에 맞는 Vector를 직접 디자인 하여 개발하는 것이 좋다. 하지만 프로토 타입을 개발하는 단계에서는이 사안이 논의 되지 않고, 자바가 제공하는 Vector를 이용하여 그냥 개발 되었을 것이다.
최적화 단계에서 저 사안이 고려 될 텐데, 문제는 Vector와 동일한 인터페이스를 갖고 최적화된 Class인 EnhancedVector를 개발 하고 싶어도, 우리의 EnhancedClass는 Vector의 묵시적 Interface를 구현할 방법이 없기 때문에, 어쩔수 없이 Vector 그 자체를 상속 받아야 한다는 것이다.
class EnhancedVector implements Vector{….
자바는 이것을 불허한다.
class EnhancedVector extends Vector{…
이렇게 할 수 밖에 없다.
우리는 Vector의 묵시적 인터페이스만이 필요한데, 구현까지 받게 되어 버린다. 그러면 모든 :EnhancedVector는 Vector가 자체적으로 처리하기 위해 가지고 있던 자료구조를 가지게 될것이다. 우리에겐 쓸모없는 상태정보가 모든 객체에 추가된다니 얼마나 끔찍한가.
이것은 Interface이외에 전혀 상관 없는 두 클래스를 상속관계로 만들어 버리게 되는 것이어서, 개념적으로도 옳지 않고, 부모를 제한 받는 것 역시 상당히 약점이 되는 요소라고 생각한다. 결국엔 Vector를 상속받는 프록시 클래스를 하나 디자인 하여 우리가 개발할 EnhacedVector를 인터페이스로 연결시키는 수준의 차선책을쓸수 밖에 없다. 이경우 우리는 추후에 더 좋은 Vector를 따로 개발했다 하더라도, 다른 백터 클래스로 교체하거나 혼용하는데, 어려움을 겪지 않을 것이다. 하지만 마음에 들지 않는다. 모든 프록시 객체는 필요도 없는 Vector구현을 위한 자료 구조를 가지게 될테니까 말이다. Java는왜 이것을 애초에 지원하지 않는가?
JVM과 자바 컴파일러는 Interface를 Class로 다루고 있다. 리플렉션에는 class객체로 부터, 그 클래스가 구현중인 인터페이스를 얻어오는 메소드인 getInterfaces()란 메소드를 가지고 있는데, 그 메소드가 반환하는 것은 Class<?>[] 타입으로, 자바가 Interface를 Class로서 다룬다는 것을 알 수 있다. Interface에 구현을 더한 것이 Class라는 개념에서 보았을 때 납득할 수 없는 사실이다. Interface가 훨씬더 포괄적이고 상위의 개념인데, 그보다 하위수준이고 특수화된 class로서 핸들되다니?
하지만 기계적 구현의 관점에서 보면 그 이유를 알 수 있다. 어쨌던 java는 소스를 컴파일 해서 class파일을 만들고, interface의 소스도 컴파일 했으니 class를 뱉어야 한다는 것이다. 바이트 코드 엔지니어의 관점에선 Interface도 구현의 대상일 뿐이니까.
기계적 구현의 관점에서 자바는 우리가 알고있는 객체지향의 모습과는 많이 다르다. 우리는 객체들이 자기 자신만의 고유의 행동을 가진다고 여기지만, 실제로는 static이건 그렇지 않건 객체들은 Class가 가진 메소드를 공유하여 사용한다. 다만그 결과 값이 반영되는 상태들의 위치를 담은 offset만을 따로 가지고 있어, 그 메소드가 상태를 변경하면 해당 객체의 상태만 변경된다. 객체지향 개념과 달리, 메소드는 클래스가 가지고 필드는 객체가 가지는 것이다. 어쨌든 행동의 영향(상태의 변경)이 객체 내부에 머물게 되므로 우리는 마치 객체들이 고유한 행동을 가진것 처럼 여기게 되는 것이다.
이와 마찬가지 맥락에서 우리의 개념상에 존재하는 Interface를 자바는 기계적 관점에서 편법을 사용하여 마치 Class와는 별개인, 객체사이의 의사소통 방법을 규정할 수 있는 도구인 것 처럼 보이게 한다. 객체와 통신하기 위해서는 우리가 알기론 반드시 Interface를 이용해서 대화해야 한다. 하지만 실제로 기계적 레벨에서는 Class를 통해서 이루어진다. Interface를 구현하여 Class가 나타나는 것이 아니라, Class를 제한 상속 하여 Interface를 구현한 탓이다. 보통 클래스의 class는 메소드가 호출되면 자기 자신의 메소드를 찾아 호출하면 되지만, 인터페이스 클래스는 메소드가 호출되면 자기 자신의 것이 아니라, 내부 알고리즘에 따라 호출해야할 메소드를 찾아 호출하게끔 구현되어있다. 따라서 Interface는 특수한 Class로서 JVM에 의해 취급된다. 이것은 OO의 가장 중요한 기본 단위가 Interface가 아닌 Class라는 생각의 관점에서 취급되었기 때문에 벌어진 일이 아닐까 생각한다. 이론상으로는 Class로 부터 동적으로 Interface Class를 얻는 것은 보통수준의 개발자라면 충분히 가능하다. 하지만, 애초에 Class는 자기가 가진 묵시적 Interface를 implements하고 있는 형태로 JVM에서 취급하지 않고 있기 때문에, 이 묵시적 Interface를 동적으로 얻어내 구현 한다고 하더라도, 별개의 인터페이스로 취급되어 객체는 의사소통을할 수 없을 것이다.
반면, Java Script같은 언어에서는 우린 손쉽게 특정 클래스의 묵시적 인터페이스를 구현하는 것이 가능하다. 그 이유는 JavaScript는 언어적 차원에서 아예 Interface를 제공하지 않기 때문이다. Java Script에서의 인터페이스란 함수의 이름이 동일하기만 하면 같은 행동으로 봐버리는 수준이므로, 우리는 목표 클래스가 가진 모든 함수와 같은 이름의 함수를 정의하고 그 구현을 함으로써, 그 클래스의 인터페이스만을 빌려올수 있다. 실행도중에 객체가 변경되어도, 어차피 Java Script는 메소드 이름만 똑같으면 호출해주기 때문에 잘 작동하는 것이다.