Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Ruby의 클래스, 인스턴스 및 메타클래스 풀기

Ruby Magic의 새로운 에피소드에 오신 것을 환영합니다! 이번 달 판은 두 개발자(Hi Maud!) 간의 토론으로 촉발된 주제인 메타클래스에 관한 것입니다.

메타클래스를 살펴봄으로써 Ruby에서 클래스와 인스턴스 메소드가 어떻게 작동하는지 배울 것입니다. 그 과정에서 명시적 "definee"를 전달하여 메소드를 정의하는 것과 class << self를 사용하는 것의 차이점을 발견하십시오. 또는 instance_eval . 가자!

클래스 인스턴스 및 인스턴스 메소드

Ruby에서 메타클래스가 사용되는 이유를 이해하기 위해 먼저 인스턴스 메서드와 클래스 메서드의 차이점을 살펴보겠습니다.

Ruby에서는 클래스 다른 객체를 생성하기 위한 청사진을 정의하는 객체입니다. 클래스는 해당 클래스의 모든 인스턴스에서 사용할 수 있는 메서드를 정의합니다.

클래스 내에서 메소드를 정의하면 인스턴스 메소드가 생성됩니다. 그 수업에. 해당 클래스의 향후 인스턴스에서 해당 메서드를 사용할 수 있습니다.

class User
  def initialize(name)
    @name = name
  end
 
  def name
    @name
  end
end
 
user = User.new('Thijs')
user.name # => "Thijs"

이 예에서는 User라는 클래스를 만듭니다. , 인스턴스 메소드 사용 이름이 #name 사용자의 이름을 반환합니다. 클래스를 사용하여 클래스 인스턴스를 만듭니다. user라는 변수에 저장합니다. . user 이후 user의 인스턴스입니다. 클래스에는 #name이 있습니다. 방법을 사용할 수 있습니다.

클래스는 메서드 테이블에 인스턴스 메서드를 저장합니다. . 해당 클래스의 모든 인스턴스는 해당 클래스의 메서드 테이블을 참조하여 해당 인스턴스 메서드에 액세스합니다.

클래스 개체

클래스 메서드 먼저 인스턴스를 만들 필요 없이 클래스에서 직접 호출할 수 있는 메서드입니다. 클래스 메서드는 이름 앞에 self.를 붙여서 생성합니다. 정의할 때.

클래스는 그 자체로 객체입니다. 상수는 클래스 개체를 참조하므로 여기에 정의된 클래스 메서드는 응용 프로그램의 어느 곳에서나 호출할 수 있습니다.

class User
  # ...
 
  def self.all
    [new("Thijs"), new("Robert"), new("Tom")]
  end
end
 
User.all # => [#<User:0x00007fb01701efb8 @name="Thijs">, #<User:0x00007fb01701ef68 @name="Robert">, #<User:0x00007fb01701ef18 @name="Tom">]

self.로 정의된 메소드 -접두사는 클래스의 메서드 테이블에 추가되지 않습니다. 대신 클래스의 메타클래스에 추가됩니다.

메타클래스

클래스를 제외하고 Ruby의 각 객체에는 숨겨진 메타클래스가 있습니다. 메타클래스는 싱글톤으로 단일 객체에 속합니다. 클래스의 인스턴스를 여러 개 생성하면 동일한 클래스를 공유하지만 모두 별도의 메타클래스를 갖게 됩니다.

thijs, robert, tom = User.all
 
thijs.class # => User
robert.class # => User
tom.class # => User
 
thijs.singleton_class  # => #<Class:#<User:0x00007fb71a9a2cb0>>
robert.singleton_class # => #<Class:#<User:0x00007fb71a9a2c60>>
tom.singleton_class    # => #<Class:#<User:0x00007fb71a9a2c10>>

이 예에서는 각 객체에 User 클래스가 있음을 알 수 있습니다. , 싱글톤 클래스는 서로 다른 개체 ID를 가지므로 별도의 개체입니다.

메타클래스에 접근함으로써 Ruby는 기존 객체에 직접 메소드를 추가할 수 있습니다. 이렇게 하면 개체의 클래스에 새 메서드가 추가되지 않습니다.

robert = User.new("Robert")
 
def robert.last_name
  "Beekman"
end
 
robert.last_name # => "Beekman"
User.new("Tom").last_name # => NoMethodError (undefined method `last_name' for #<User:0x00007fe1cb116408>)

이 예에서는 #last_name을 추가합니다. robert에 저장된 사용자에게 변하기 쉬운. robert User의 인스턴스입니다. , 새로 생성된 User 인스턴스 #last_name에 액세스할 수 없습니다. 메소드, robert에만 존재하므로 의 메타클래스입니다.

self이란? ?

메소드를 정의하고 리시버를 전달할 때 새 메소드는 클래스의 메소드 테이블에 추가하는 대신 수신자의 메타클래스에 추가됩니다.

tom = User.new("Tom")
 
def tom.last_name
  "de Bruijn"
end

위의 예에서 #last_name을 추가했습니다. tom에서 직접 tom을 전달하여 객체 메소드를 정의할 때 수신자로.

이것은 클래스 메서드에서도 작동하는 방식입니다.

class User
  # ...
 
  def self.all
    [new("Thijs"), new("Robert"), new("Tom")]
  end
end

여기에서 명시적으로 self를 전달합니다. .all 생성 시 수신자로 방법. 클래스 정의에서 self 클래스를 나타냅니다(user 이 경우) 따라서 .all 메소드가 User에 추가됩니다. 의 메타클래스입니다.

user 때문에 상수에 저장된 객체이므로 참조할 때마다 동일한 객체와 동일한 메타클래스에 액세스합니다.

메타클래스 열기

우리는 클래스 메소드가 클래스 객체의 메타클래스에 있는 메소드라는 것을 배웠습니다. 이것을 알면 이전에 본 적이 있을 수 있는 클래스 메서드를 만드는 다른 기술을 살펴보겠습니다.

class << self

약간 스타일이 바뀌었지만 일부 라이브러리는 class << self를 사용합니다. 클래스 메서드를 정의합니다. 이 구문 트릭은 현재 클래스의 메타클래스를 열고 직접 상호작용합니다.

class User
  class << self
    self # => #<Class:User>
 
    def all
      [new("Thijs"), new("Robert"), new("Tom")]
    end
  end
end
 
User.all # => [#<User:0x00007fb01701efb8 @name="Thijs">, #<User:0x00007fb01701ef68 @name="Robert">, #<User:0x00007fb01701ef18 @name="Tom">]

이 예제는 User.all이라는 클래스 메소드를 생성합니다. User에 메소드를 추가하여 의 메타클래스입니다. 이전에 본 것처럼 메서드에 대한 수신자를 명시적으로 전달하는 대신 self를 설정합니다. user에게 User 대신 의 메타클래스 그 자체.

이전에 배웠듯이 명시적 수신기가 없는 모든 메서드 정의는 현재 클래스의 인스턴스 메서드로 추가됩니다. 블록 내에서 현재 클래스는 User입니다. 의 메타클래스(#<Class:User> ).

instance_eval

또 다른 옵션은 instance_eval을 사용하는 것입니다. , 이는 한 가지 주요 차이점으로 동일한 작업을 수행합니다. 클래스의 메타클래스가 블록에 정의된 메소드를 수신하더라도 self 기본 클래스에 대한 참조로 남아 있습니다.

class User
  instance_eval do
    self # => User
 
    def all
      [new("Thijs"), new("Robert"), new("Tom")]
    end
  end
end
 
User.all # => [#<User:0x00007fb01701efb8 @name="Thijs">, #<User:0x00007fb01701ef68 @name="Robert">, #<User:0x00007fb01701ef18 @name="Tom">]

이 예에서는 User에 대한 인스턴스 메서드를 정의합니다. 의 메타클래스는 이전과 같지만 self 여전히 User를 가리킵니다. . 일반적으로 동일한 객체를 가리키지만 "default definee" 및 self 다른 개체를 가리킬 수 있습니다.

우리가 배운 것

클래스는 메서드를 가질 수 있는 유일한 개체이며 인스턴스 메서드는 실제로 개체의 메타클래스에 있는 메서드라는 것을 배웠습니다. 우리는 class << self 단순히 self를 교환합니다. 메타클래스에서 메소드를 정의할 수 있도록 하며 instance_eval 대부분 동일한 작업을 수행합니다(그러나 self를 건드리지 않고 ).

명시적으로 메타클래스를 사용하지는 않겠지만 Ruby는 내부적으로 메타클래스를 광범위하게 사용합니다. 메서드를 정의할 때 어떤 일이 발생하는지 알면 Ruby가 그렇게 동작하는 이유와 클래스 메서드에 self. 접두사를 붙여야 하는 이유를 이해하는 데 도움이 됩니다. ).

읽어 주셔서 감사합니다. 읽은 내용이 마음에 들면 Ruby Magic을 구독하면 한 달에 한 번 정도 새 기사가 게시될 때 이메일을 받을 수 있습니다.