제목 한 번 거창하다. 저렇게 거창한 제목을 쓰고나면 '잘 써야지' 하는 생각보다도 '개념적 고찰이 뭐더라' 하는 생각에 빠진다. 뭐, 그러려니 하고 그냥 넘어가자. 제목에 너무 큰 의미를 갖지 말자.
객체 지향 프로그래밍, 나는 자바 언어를 주로 사용하고 요즘들어 필요에 의해 C++ 언어를 쓰고 있다. 둘다 비슷해요- 라고 전엔 자신있게 얘기했었는데 지금은 뭐랄까... 아, 두 언어의 차이는 오늘의 주제가 아니다. 넘어가자. 어쨌든, 두 언어는 모두 객체 지향 프로그래밍을 가능하게 하는 언어임에는 틀림없다.
사실, 객체에 대한 정의는 대부분의 객체 지향 언어들이 유사할 것이다. 오늘 이야기하고자 하는 내용은, 그것이 어떻게 동작하는가에 대한 가벼운 생각이다. 갑자기 이런 생각을 글로 쓰게 된 것은, 내가 최근에 루비라는 언어를 접했기 때문이다.
자바 언어에서는 클래스를 다음과 같이 정의한다.
그리고 루비에서는 다음과 같이 정의한다.
두 언어가 정의한 클래스는 동일하게 동작한다. getter 메소드를 통해 재생 횟수를 확인할 수 있고 재생 메소드를 호출하면 재생 횟수가 증가한다. 다시, 클래스의 상태와 행위가 동일하다. 동일하게 동작하지는 않는다.
무슨 말일까? 나는 말을 번복했다. 동일하게 동작하지 않는다고 고쳤다. 동일하게 정의한 클래스가 동일하게 동작하지 않는다고? 나는 오늘 이 얘기가 하고 싶어서 입이 실룩거렸다. 동일하게 동작하지 않는다는 것은, 자바와 루비가 실행 시간에 동작하는 방법에서 오는 차이에서 비롯되고, 그것은 똑같은 결과를 나타내지만 실제로는 커다란 차이가 있기 때문이다.
자바로 작성된 클래스로부터 인스턴스를 생성하고 인스턴스의 메소드를 호출하는 과정을 보면 다음과 같다.
루비는 다음과 같이 인스턴스를 생성하고 메시지를 보낸다.
두 언어가 동작하는 가장 큰 차이는 호출(CALL)과 전송(SEND)이다. 자바는 메소드를 호출한다. 메모리상에 생성된 Player 클래스의 인스턴스는 변수 p 에 의해 가리켜지고, p 로부터 play 메소드를 호출하는 것은 Player 클래스 정의에 따라 play 메소드의 구현 코드가 있는 메모리 주소로 점프한다. 그 곳에는 play 메소드의 구현 코드가 있고, 그 부분부터 로직을 수행하고 수행이 완료되면 호출 지점으로 돌아온다.
루비 역시 메모리상에 생성된 Player 클래스의 인스턴스를 변수 p 로 가리키고 있다. 그러나 이후 처리 과정에서 차이가 발생한다. p.play 는 play 메소드로 점프하는 것이 아니다. p 가 가리키는 객체로 메시지를 전송한다. 인스턴스는 이 메시지를 받아서 적절한 처리를 한다. '음, 이 메시지는 play 기능을 요청하고 있군' 따라서 play 구현 코드를 수행하고 수행 결과를 반환한다.
호출과 전송은 커다란 차이가 있다. 빠른 퍼포먼스에 목이 마른 개발자는 '이 차이는, 실행 속도에 커다란 영향을 미치겠군' 이라고 생각할 것이다. 아, 물론 그렇기도 하겠지. 정말 큰 차이는 그게 아니다. 자, 루비의 예에서 변수 p 가 어떤 타입으로 지정되어있는지 찾아보자. 없다. 그렇다. 타입이 지정되지 않았다. 자바와 C++ 만 봐온 사람들로선 다소 어색할만한 부분이다.
호출은, 클래스의 정의로부터 메소드가 정의된 메모리 주소를 알고서 그리로 점프한다. 타입을 모르면 어디로 점프해야할지 알 방법이 없다. 컴파일러는 우수해서 이러한 상황에선 컴파일 에러를 낸다. 전송은 어떨까? 일단 그 객체로 메시지를 보내보는거다. 되면 되고, 아님 말고- 이런 마인드도 좋다. 사실이 그렇다. 루비에서 모든 객체는 메시지를 수신하여 적절한 처리를 한다. 따라서 정말 그 메소드가 존재하는지는 실행 시간까지 중요하지 않다. 만약 처리할 수 없는 메시지가 도착하였다면, 기본적으로는 죽어버리지만 적절한 우회 처리를 구현할 수도 있다.
자, 다시 한 번 말하지만, 클래스의 상태와 행위는 동일하다. 그러나 동일하게 동작하지는 않는다. 수행 결과를 놓고 이야기하는 것이 아니라, 수행되는 과정을 이야기하고 싶었던 것이다. 호출(CALL)과 전송(SEND)은 큰 차이가 있다. 오늘은 비록 '차이가 있다' 로 마무리 지으려 하고 있지만, 이 차이는 보다 상위 레벨의 설계를 가능하게 한다고 나는 생각한다. 에이전트를 이야기하기엔 시간이 너무 늦었고, 체력은 바닥을 기고 있다.
객체 지향 프로그래밍, 나는 자바 언어를 주로 사용하고 요즘들어 필요에 의해 C++ 언어를 쓰고 있다. 둘다 비슷해요- 라고 전엔 자신있게 얘기했었는데 지금은 뭐랄까... 아, 두 언어의 차이는 오늘의 주제가 아니다. 넘어가자. 어쨌든, 두 언어는 모두 객체 지향 프로그래밍을 가능하게 하는 언어임에는 틀림없다.
객체는 내부 상태가 있고, 상태를 질의하거나 변경시킬 수 있는 메소드가 있다.
사실, 객체에 대한 정의는 대부분의 객체 지향 언어들이 유사할 것이다. 오늘 이야기하고자 하는 내용은, 그것이 어떻게 동작하는가에 대한 가벼운 생각이다. 갑자기 이런 생각을 글로 쓰게 된 것은, 내가 최근에 루비라는 언어를 접했기 때문이다.
자바 언어에서는 클래스를 다음과 같이 정의한다.
public class Player {
private int _playCount;
public int getPlayCount() {
return _playCount;
}
public void play() {
_playCount++;
}
}
private int _playCount;
public int getPlayCount() {
return _playCount;
}
public void play() {
_playCount++;
}
}
그리고 루비에서는 다음과 같이 정의한다.
class Player
def initialize
@playCount = 0
end
def get_play_count
@playCount
end
def play
@playCount = @playCount + 1
end
end
def initialize
@playCount = 0
end
def get_play_count
@playCount
end
def play
@playCount = @playCount + 1
end
end
두 언어가 정의한 클래스는 동일하게 동작한다. getter 메소드를 통해 재생 횟수를 확인할 수 있고 재생 메소드를 호출하면 재생 횟수가 증가한다. 다시, 클래스의 상태와 행위가 동일하다. 동일하게 동작하지는 않는다.
무슨 말일까? 나는 말을 번복했다. 동일하게 동작하지 않는다고 고쳤다. 동일하게 정의한 클래스가 동일하게 동작하지 않는다고? 나는 오늘 이 얘기가 하고 싶어서 입이 실룩거렸다. 동일하게 동작하지 않는다는 것은, 자바와 루비가 실행 시간에 동작하는 방법에서 오는 차이에서 비롯되고, 그것은 똑같은 결과를 나타내지만 실제로는 커다란 차이가 있기 때문이다.
자바로 작성된 클래스로부터 인스턴스를 생성하고 인스턴스의 메소드를 호출하는 과정을 보면 다음과 같다.
Player p = new Player();
p.play();
int count = p.getPlayCount();
p.play();
int count = p.getPlayCount();
루비는 다음과 같이 인스턴스를 생성하고 메시지를 보낸다.
p = Player.new
p.play
count = p.get_play_count
p.play
count = p.get_play_count
두 언어가 동작하는 가장 큰 차이는 호출(CALL)과 전송(SEND)이다. 자바는 메소드를 호출한다. 메모리상에 생성된 Player 클래스의 인스턴스는 변수 p 에 의해 가리켜지고, p 로부터 play 메소드를 호출하는 것은 Player 클래스 정의에 따라 play 메소드의 구현 코드가 있는 메모리 주소로 점프한다. 그 곳에는 play 메소드의 구현 코드가 있고, 그 부분부터 로직을 수행하고 수행이 완료되면 호출 지점으로 돌아온다.
루비 역시 메모리상에 생성된 Player 클래스의 인스턴스를 변수 p 로 가리키고 있다. 그러나 이후 처리 과정에서 차이가 발생한다. p.play 는 play 메소드로 점프하는 것이 아니다. p 가 가리키는 객체로 메시지를 전송한다. 인스턴스는 이 메시지를 받아서 적절한 처리를 한다. '음, 이 메시지는 play 기능을 요청하고 있군' 따라서 play 구현 코드를 수행하고 수행 결과를 반환한다.
호출과 전송은 커다란 차이가 있다. 빠른 퍼포먼스에 목이 마른 개발자는 '이 차이는, 실행 속도에 커다란 영향을 미치겠군' 이라고 생각할 것이다. 아, 물론 그렇기도 하겠지. 정말 큰 차이는 그게 아니다. 자, 루비의 예에서 변수 p 가 어떤 타입으로 지정되어있는지 찾아보자. 없다. 그렇다. 타입이 지정되지 않았다. 자바와 C++ 만 봐온 사람들로선 다소 어색할만한 부분이다.
호출은, 클래스의 정의로부터 메소드가 정의된 메모리 주소를 알고서 그리로 점프한다. 타입을 모르면 어디로 점프해야할지 알 방법이 없다. 컴파일러는 우수해서 이러한 상황에선 컴파일 에러를 낸다. 전송은 어떨까? 일단 그 객체로 메시지를 보내보는거다. 되면 되고, 아님 말고- 이런 마인드도 좋다. 사실이 그렇다. 루비에서 모든 객체는 메시지를 수신하여 적절한 처리를 한다. 따라서 정말 그 메소드가 존재하는지는 실행 시간까지 중요하지 않다. 만약 처리할 수 없는 메시지가 도착하였다면, 기본적으로는 죽어버리지만 적절한 우회 처리를 구현할 수도 있다.
자, 다시 한 번 말하지만, 클래스의 상태와 행위는 동일하다. 그러나 동일하게 동작하지는 않는다. 수행 결과를 놓고 이야기하는 것이 아니라, 수행되는 과정을 이야기하고 싶었던 것이다. 호출(CALL)과 전송(SEND)은 큰 차이가 있다. 오늘은 비록 '차이가 있다' 로 마무리 지으려 하고 있지만, 이 차이는 보다 상위 레벨의 설계를 가능하게 한다고 나는 생각한다. 에이전트를 이야기하기엔 시간이 너무 늦었고, 체력은 바닥을 기고 있다.
이 글의 트랙백 주소는 http://semix2.tistory.com/trackback/313 입니다
오- 그렇구나. '호출'과 '전송'
선생님이나 교수님께 호출되서 혼나는 것보다 지시사항을 받는게 낫죠.
(왜 이런 생각이 드는거지?-_-)
갑자기 딴소리를 했는데 쨋든, 잼있네요 ^_^
뭐, 같은 소리야. 지시 사항을 받게되면 받는 입장에서는 [선택권]이 주어지거든. 거기서 가장 큰 차이가 나는거지.