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

루비 매직의 새로운 에피소드에 오신 것을 환영합니다! 이 달의 에디션은 모든 메타 클래스에 관한 것입니다,두 개발자 사이의 토론에 의해 촉발 주제(안녕 마 우드 스웨!).

메타클래스 검사를 통해 클래스와 인스턴스 메소드가 루비에서 어떻게 작동하는지 알아보겠습니다. 이 과정에서 명시적인”정의자”를 전달하여 메서드를 정의하는 것과class << self또는instance_eval을 사용하는 것의 차이점을 알아보십시오. 가자!

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

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

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

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

1 2 3 4 5 6 7 8 9 10 11 12
class User def initialize(name) @name = name end def name @name end end user = User.new('Thijs') user.name # => "Thijs" 

이 예제에서는 사용자 이름을 반환하는#name이라는 인스턴스 메서드를 사용하여User라는 클래스를 만듭니다. 클래스를 사용하여 클래스 인스턴스를 만들어user이라는 변수에 저장합니다. userUser클래스의 인스턴스이므로#name메서드를 사용할 수 있습니다.

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

클래스 개체

클래스 메서드는 먼저 인스턴스를 만들지 않고도 클래스에서 직접 호출할 수 있는 메서드입니다. 클래스 메서드를 정의할 때 이름 앞에self.을 붙여서 만듭니다.

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

1 2 3 4 5 6 7 8 9
class User # ... def self.all end end User.all # => 

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

메타 클래스

클래스 외에도 루비의 각 객체에는 숨겨진 메타 클래스가 있습니다. 메타 클래스는 단일 객체에 속한다는 것을 의미하는 싱글 톤입니다. 클래스의 여러 인스턴스를 만드는 경우 동일한 클래스를 공유하지만 모두 별도의 메타 클래스를 갖습니다.

1 2 3 4 5 6 7 8 9
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가 있지만 단일 클래스는 서로 다른 객체 식별자를 가지므로 별도의 객체라는 것을 알 수 있습니다.

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

1 2 3 4 5 6 7 8
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>) 

이 예제에서는robert변수에 저장된 사용자에게#last_name을 추가합니다. robertUser의 인스턴스이지만 새로 만든User인스턴스는robert의 메타클래스에만 존재하므로#last_name메서드에 액세스할 수 없습니다.

자기란 무엇인가?

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

1 2 3 4 5
tom = User.new("Tom") def tom.last_name "de Bruijn" end 

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

이것은 또한 클래스 메소드에 대해 작동하는 방식입니다.

1 2 3 4 5 6 7
class User # ... def self.all end end 

여기서는.all메서드를 만들 때self을 수신기로 명시적으로 전달합니다. 클래스 정의에서self은 클래스(이 경우User)를 참조하므로.all메서드가User의 메타 클래스에 추가됩니다.

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

메타클래스 열기

클래스 메서드는 클래스 개체의 메타클래스에 있는 메서드라는 것을 배웠습니다. 이것을 알고,우리는 당신이 전에 본 것 같은 클래스 메소드를 만드는 몇 가지 다른 기술을 살펴 보겠습니다.

클래스<<자체

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

1 2 3 4 5 6 7 8 9 10 11
class User class << self self # => #<Class:User> def all end end end User.all # => 

이 예제에서는User의 메타 클래스에 메서드를 추가하여User.all이라는 클래스 메서드를 만듭니다. 이전에 보았 듯이 메서드에 대한 수신기를 명시 적으로 전달하는 대신selfUser자체 대신User의 메타 클래스로 설정합니다.

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

인스턴스

또 다른 옵션은instance_eval을 사용하는 것입니다. 클래스의 메타클래스가 블록에 정의된 메서드를 수신하지만self은 주 클래스에 대한 참조로 남아 있습니다.

1 2 3 4 5 6 7 8 9 10 11
class User instance_eval do self # => User def all end end end User.all # => 

이 예제에서는 이전과 같이User의 메타 클래스에 인스턴스 메서드를 정의하지만self은 여전히User를 가리 킵니다. 일반적으로 동일한 개체를 가리키지만”기본 정의”와self은 다른 개체를 가리킬 수 있습니다.

우리가 배운 것

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

메타클래스를 명시적으로 사용하지는 않겠지만 루비는 메타클래스를 광범위하게 사용한다. 메소드를 정의 할 때 어떤 일이 발생하는지 알면 루비가 왜 그렇게 행동하는지(그리고 왜 클래스 메소드에self.을 접두사로 지정해야하는지)이해하는 데 도움이 될 수 있습니다.

읽어 주셔서 감사합니다. 당신은 당신이 읽은 것을 좋아하는 경우에,당신은 우리가 한 달에 한 번에 대한 새로운 기사를 게시 할 때 전자 메일을받을 루비 매직에 가입하실 수 있습니다.



+