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

Ruby가 객체를 생성하는 방식 변경하기

Ruby를 훌륭하게 만드는 것 중 하나는 거의 모든 것을 필요에 맞게 사용자 정의할 수 있다는 것입니다. 이것은 유용하면서도 위험합니다. 발에 총을 쏘는 것은 쉽지만 신중하게 사용하면 매우 강력한 솔루션이 될 수 있습니다.

Ruby Magic에서는 유용함과 위험함이 훌륭한 조합이라고 생각합니다. Ruby가 객체를 생성하고 초기화하는 방법과 기본 동작을 수정하는 방법을 살펴보겠습니다.

클래스에서 새 개체 생성의 기본

시작하기 위해 Ruby에서 객체를 생성하는 방법을 살펴보겠습니다. 새 객체를 생성하려면 (또는 인스턴스 ), 우리는 new라고 부릅니다. 수업에. 다른 언어와 달리 new 언어 자체의 키워드가 아니라 다른 메소드와 마찬가지로 호출되는 메소드입니다.

class Dog
end
 
object = Dog.new

새로 생성된 개체를 사용자 지정하기 위해 new에 인수를 전달할 수 있습니다. 방법. 인수로 전달된 것은 무엇이든 이니셜라이저로 전달됩니다.

class Dog
  def initialize(name)
    @name = name
  end
end
 
object = Dog.new('Good boy')

다시 말하지만, 다른 언어와 달리 Ruby의 초기화 프로그램은 특별한 구문이나 키워드가 아닌 메소드일 뿐입니다.

이를 염두에두고 다른 Ruby 방법과 마찬가지로 이러한 방법을 엉망으로 만드는 것이 불가능해야합니까? 물론입니다!

단일 개체의 동작 수정

하위 클래스에서 메서드가 재정의되더라도 특정 클래스의 모든 개체가 항상 로그 문을 인쇄하도록 하고 싶다고 가정해 보겠습니다. 이를 수행하는 한 가지 방법은 개체의 싱글톤 클래스에 모듈을 추가하는 것입니다.

module Logging
  def make_noise
    puts "Started making noise"
    super
    puts "Finished making noise"
  end
end
 
class Bird
  def make_noise
    puts "Chirp, chirp!"
  end
end
 
object = Bird.new
object.singleton_class.include(Logging)
object.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise

이 예에서 Bird 개체는 Bird.new를 사용하여 생성됩니다. 및 Logging 모듈은 싱글톤 클래스를 사용하여 결과 개체에 포함됩니다.

싱글톤 클래스란 무엇입니까?

Ruby는 단일 객체에 고유한 메서드를 허용합니다. 이를 지원하기 위해 Ruby는 객체와 실제 클래스 사이에 익명 클래스를 추가합니다. 메서드가 호출되면 싱글톤 클래스에 정의된 메서드가 실제 클래스의 메서드보다 우선합니다. 이러한 싱글톤 클래스는 모든 개체에 고유하므로 여기에 메서드를 추가해도 실제 클래스의 다른 개체에는 영향을 미치지 않습니다. 프로그래밍 Ruby 가이드에서 클래스와 객체에 대해 자세히 알아보세요.

객체가 생성될 때마다 각 객체의 싱글톤 클래스를 수정하는 것은 약간 번거롭습니다. 이제 Logging 생성된 모든 개체에 대해 클래스를 이니셜라이저에 추가합니다.

module Logging
  def make_noise
    puts "Started making noise"
    super
    puts "Finished making noise"
  end
end
 
class Bird
  def initialize
    singleton_class.include(Logging)
  end
 
  def make_noise
    puts "Chirp, chirp!"
  end
end
 
object = Bird.new
object.make_noise
# Started making noise
# Chirp, chirp!
# Finished making noise

이것이 잘 작동하지만 Bird의 하위 클래스를 생성하면 , Duck처럼 , 초기화 프로그램은 super를 호출해야 합니다. Logging 유지 행동. super를 적절하게 호출하는 것이 항상 좋은 생각이라고 주장할 수 있지만 메소드가 재정의될 때마다 필요하지 않은 그것.

super를 호출하지 않으면 하위 클래스에서 Logger를 포함하지 않습니다. 클래스:

class Duck < Bird
  def initialize(name)
    @name = name
  end
 
  def make_noise
    puts "#{@name}: Quack, quack!"
  end
end
 
object = Duck.new('Felix')
object.make_noise
# Felix: Quack, quack!

대신 Bird.new를 재정의합시다. . 앞서 언급했듯이 new 클래스에 구현된 메서드일 뿐입니다. 따라서 이를 재정의하고 super를 호출하고 새로 생성된 객체를 필요에 맞게 수정할 수 있습니다.

class Bird
  def self.new(*arguments, &block)
    instance = super
    instance.singleton_class.include(Logging)
    instance
  end
end
 
object = Duck.new('Felix')
object.make_noise
# Started making noise
# Felix: Quack, quack!
# Finished making noise

하지만 make_noise를 호출하면 어떻게 될까요? 이니셜라이저에서? 불행히도 싱글톤 클래스에는 Logging이 포함되어 있지 않기 때문에 모듈은 아직 원하는 출력을 얻지 못합니다.

다행히도 해결책이 있습니다. 기본 .new를 생성할 수 있습니다. allocate를 호출하여 처음부터 동작 .

class Bird
  def self.new(*arguments, &block)
    instance = allocate
    instance.singleton_class.include(Logging)
    instance.send(:initialize, *arguments, &block)
    instance
  end
end

allocate 호출 클래스의 초기화되지 않은 새 개체를 반환합니다. 따라서 이후에 추가 동작을 포함할 수 있으며 그런 다음에만 initialize를 호출합니다. 해당 개체에 대한 메서드입니다. (왜냐하면 initialize 기본적으로 비공개이므로 send를 사용해야 합니다. 이를 위해).

Class#allocate에 대한 진실

다른 방법과 달리 allocate를 재정의할 수 없습니다. . Ruby는 allocate 메소드를 전달하는 기존 방식을 사용하지 않습니다. 내부적으로. 결과적으로 allocate를 재정의하면 됩니다. new도 재정의하지 않고 작동하지 않습니다. 그러나 allocate 직접적으로 Ruby는 재정의된 메서드를 호출합니다. Class#new에 대해 자세히 알아보기 및 Class#allocate Ruby 문서에 있습니다.

이 작업을 수행하는 이유는 무엇입니까?

많은 경우와 마찬가지로 Ruby가 클래스에서 객체를 생성하는 방식을 수정하면 위험할 수 있으며 예기치 않은 방식으로 일이 중단될 수 있습니다.

그럼에도 불구하고 객체 생성을 변경하는 유효한 사용 사례가 있습니다. 예를 들어 ActiveRecord는 allocate를 사용합니다. 다른 init_from_db 사용 저장되지 않은 개체를 빌드하는 것과 반대로 데이터베이스에서 개체를 만들 때 초기화 프로세스를 변경하는 방법입니다. 또한 allocate을 사용합니다. becomes를 사용하여 서로 다른 단일 테이블 상속 유형 간에 레코드를 변환합니다. .

가장 중요한 것은 객체 생성을 가지고 놀면서 Ruby에서 작동하는 방식에 대한 더 깊은 통찰력을 얻고 다양한 솔루션에 마음을 열 수 있다는 것입니다. 기사가 도움이 되었기를 바랍니다.

Ruby의 기본 객체 생성 방식을 변경하여 구현한 사항에 대해 듣고 싶습니다. 주저하지 말고 @AppSignal에 의견을 트윗해주세요.