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에 의견을 트윗해주세요.