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을 구독하면 한 달에 한 번 정도 새 기사가 게시될 때 이메일을 받을 수 있습니다.