Class hierarchy in Ruby

Tags:

앞서 포스팅한 A little ruby- Ch3. Metaclass Hackery 에 실린 내용을 바탕으로 간단한 클래스의 hierarchy를 정리해보겠습니다. 다음과 같은 클래스 Foo가 있다고 해보죠.

class Foo
  def initialize
    @i = 0
  end
  
  def bar
  end
  
  def Foo.class_bar
    @j = 0
  end
  
  def Foo.new
     super
  end
end

여기서 @i 는 인스턴스 변수, @j 는 Foo.class_bar 내에서 정의되었으므로 클래스 변수입니다. 그리고 상식적으로 다음과 같은 일이 발생함을 알 수 있습니다.

f = Foo.new
f.class => Foo
Foo.class => Class

이제 하나씩 설명해보겠습니다. 먼저, f는 Foo의 인스턴스 변수이므로 PickAxe에 나왔다시피, 다음과 같이 구성됩니다.

f ->  Foo    -> (Foo)
@i      bar         new
        @j         class_bar

즉, @i는 f에 있는 인스턴스 변수이며 f.bar 는 f에서 오른쪽 화살표로 따라간 뒤 Foo에서 인스턴스 메소드를 찾습니다. 마찬가지로 @j 는 Foo에 정의되며, 클래스 메소드인 Foo.new는 (Foo)라는 Foo의 메타클래스에 있는 new를 호출하게 됩니다.

또한, 우리는 Foo가 Object를 상속함을 알고 있습니다. 마찬가지로, 일관성을 위해 (Foo)는 (Object)를 상속합니다. 그러면,

      Object -> (Object)
       ^          ^
       |          |
f ->  Foo    -> (Foo)
@i      bar         new
        @j         class_bar

여기가지 완성입니다. 다음, f.class를 호출하면 Foo가 나와야합니다. 이는 Object에 있는 인스턴스 메소드인 class에 해당합니다. 이걸 채워넣으면

      Object -> (Object)
       ^ class    ^
       |          |
f ->  Foo    -> (Foo)
@i      bar         new
        @j         class_bar

가 되겠죠. 이제 Foo.class를 생각해봅시다. 만약 Foo.class를 f.class와 마찬가지로 한다면 Foo.class 의 결과는 “meta Object”가 되며 이는 매우 직관적이지 않습니다. 따라서, Class라는 것을 하나 놓습니다.

                 Class   -> (Class)
                  ^  new         new
                  |
      Object -> (Object)
       ^ class    ^
       |          |
f ->  Foo    -> (Foo)
@i      bar         new
        @j         class_bar

그리고 Class는 new를 가집니다. 자 이제 여기서, 앞서의 코드를 떠올려보면 Foo.new는 super를 호출하게 되어있습니다. 여기, Class에 있는 new라는 메소드가 그때 불려지는 new에 해당하게 되는 것입니다. 즉, 인스턴스를 생성할때 super를 부르면 불리게 되는 메소드가 Class.new입니다.

다시 원점으로 돌아와서, 우리는 Foo.class가 Class라고 나오기를 원합니다. 이를 해결하는 방법은 모든 클래스가 Object를 상속함을 이용하여 다음과 같이 하는 것입니다.

                 Object     (Object)
                  ^  class      ^
                  |             |
                 Class   -> (Class)
                  ^  new         new
                  |
      Object -> (Object)
       ^ class    ^
       |          |
f ->  Foo    -> (Foo)
@i      bar         new
        @j         class_bar

원래는 Class에서 다시 밑에있는 Object로 화살표를 아래쪽으로 그려야하지만 표현이 안되니 위에 다시 그렿습니다. 다시 말해 그림 맨 위의 Object와 중간의 Object는 서로 같은것입니다. 아무튼.. 그래서 이처럼 Foo.class 는 (Foo)로 메소드를 찾으러 갔다가 못찾고, 그 부모인 (Object)로 갔다가 못찾고, Class에서 못찾고, Object에 이르러 class를 찾게됩니다. 이를 통해 Foo.class가 “meta Object”가 아니라 “Class”라고 답이 나올 수 있게 됩니다.

다음, new에 대해서 생각해보죠. Foo.new는 Foo의 인스턴스를 만듭니다. 이 과정에서 Foo.new라는 호출은 Foo에게 메시지가 가고, 메소드를 부르기 위해 우측으로 가서, Foo의 메타클래스인 (Foo)의 new를 찾아 인스턴스를 생성하게 됩니다.

하지만 Foo 라는 클래스 자체는 어떻게 만들까요? 바로 이 과정에서 (Class).new 가 쓰입니다. 예를 들어, Bar라는 Foo의 서브 클래스를 만들때에는

Bar = Class.new(Foo)

라고 할수 있습니다. 그리고 이는 아시다시피 class Bar < Foo; end 와 같은 의미입니다. 이 과정에서 Class.new 는 Class로 갔다가, Class에 대한 클래스 메소드를 부르기 위해 (Class)로 가고 거기에 있는 new를 쓰게 되는 것입니다. 이제 끝일까요? ㅎㅎ 아닙니다. 왜냐하면, [code lang="ruby"] Foo.class.superclass => Module
[/code]

이기 때문이죠 ㅠㅠ 아아.. 그러나 이것까지 그릴 재주는 없고, 대강 Foo 가 Module을 상속받고 있음을 잘 끼워넣으면 될 것입니다. ㅎㅎ 아무튼 이런 내용 전반에 대한 이해가 언어를 잘 아는데 도움이 되고 특히 메타 프로그래밍시에 많은 도움이 된다고 생각해서 올립니다.

Comments

3 responses to “Class hierarchy in Ruby”

  1. 공성식 Avatar

    안녕하세요?
    아주 그림을 잘 그려주셨네요.

    한 가지, 잘못된 것이 있어서 지적하고자 합니다.
    (혹시 단순히 표기의 오류라면 죄송합니다.)
    @j와 @@j가 혼동되는군요.
    class variable과 class instance variable은 전혀 다릅니다.
    위의 예에서 다음과 같이 된 부분은 class instance variable입니다.

    def Foo.class_bar
    @j = 0
    end

    그런데 그 아래의 다이어크램에 보면 @@j가 나와 있습니다. 만약 @@j라는 게 코드에 있었다면 이것은 분명 class variable입니다.
    class instance variable은 per-class이고 class variable은 per-hierarchy입니다.
    제 개인적인 생각으로는 class variable은 OO적이지 않은 것 같습니다.
    너무 글로벌한 느낌이 들어서요.
    다만 notation 상 그게 더 편하기 때문에 자주 사용되는 것 같습니다.
    다른 언어에서는 볼 수 없던 좀 이상한 것입니다.

    루비의 클래스 계층은 Chris Pine의 Understanding Ruby’s Object Model을 보면 이해가 쉽더군요.
    (http://www.ruby-doc.org/docs/Understanding%20Ruby's%20Object%20Model/ChrisPine_UROM.ppt)
    PickAxe도 설명이 잘 돼 있긴 하지만요.

  2. MKSeo Avatar
    MKSeo

    제가 완전히 잘못알고 있군요;;
    가르쳐주신 ppt 감사히 읽어보겠습니다. ^^

  3. MKSeo Avatar
    MKSeo

    @@j는 고쳤습니다. ^^

Leave a Reply

Your email address will not be published. Required fields are marked *