Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Định nghĩa lại Ruby Object Instantiation: Hướng dẫn Tùy chỉnh Creation

Một điều làm Ruby tuyệt vời là customize gần như mọi thứ theo nhu cầu. Cả hữu ích và nguy hiểm. Dễ bắn mình vào chân, nhưng dùng cẩn thận tạo ra powerful solutions.

Tại Ruby Magic, chúng tôi nghĩ useful và dangerous là combo tuyệt vời. Hãy xem Ruby tạo và initialize object như thế nào và modify default behavior như thế nào.

Basics of Creating New Objects from Classes

Bắt đầu, xem tạo object trong Ruby. Tạo object (hoặc instance), gọi new trên class. Khác ngôn ngữ khác, new không phải keyword ngôn ngữ, mà method gọi như method khác.

class Dog
end
object = Dog.new

Để customize object mới tạo, pass argument cho new. Cái gì pass as argument, sẽ pass cho initializer.

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

Lại khác ngôn ngữ khác, initializer trong Ruby cũng chỉ là method thay vì special syntax/keyword.

Với điều đó, không nên possible mess around với những method đó, như với bất kỳ Ruby method khác? Tất nhiên!

Modify Behavior of Single Object (Sửa behavior single object)

Giả sử muốn ensure tất cả object của class cụ thể luôn print log statements, ngay cả khi method override trong subclass. Cách là thêm module vào singleton class của object.

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

Ví dụ này, Bird object tạo bằng Bird.new, module Logging include vào object kết quả dùng singleton class.

Singleton Class là gì?

Ruby cho phép method unique cho single object. Để support, Ruby thêm anonymous class giữa object và actual class. Khi method gọi, method định nghĩa trên singleton class có precedence hơn method actual class. Singleton classes unique cho mỗi object, nên thêm method vào không affect object khác của actual class. Tìm hiểu thêm về class và object trong Programming Ruby guide.

Hơi cumbersome modify singleton class mỗi object khi tạo. Move inclusion Logging class vào initializer để thêm cho mọi object tạo ra.

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

Cách này work tốt, nhưng nếu tạo subclass Bird như Duck, initializer của nó cần gọi super để retain Logging behavior. Mặc dù có thể argue luôn good idea gọi super khi override method, hãy tìm cách không require nó.

Nếu không gọi super từ subclass, mất inclusion Logger class:

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!

Thay vào đó, override Bird.new. Như nói, new chỉ là method implement trên class. Vậy override nó, gọi super, modify object mới tạo theo nhu cầu.

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

Nhưng, nếu gọi make_noise trong initializer? Thật không may, vì singleton class chưa include Logging module, ta không get desired output.

May mắn có solution: Có thể tạo default .new behavior từ đầu bằng gọi allocate.

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

Gọi allocate trả về object mới, uninitialized của class. Sau đó include additional behavior, sau đó mới gọi initialize trên object đó. (Vì initialize private mặc định, phải dùng send).

Sự thật về Class#allocate

Khác method khác, không thể override allocate. Ruby không dùng conventional method dispatch cho allocate internally. Kết quả, chỉ override allocate không override new không work. Tuy nhiên, nếu gọi allocate trực tiếp, Ruby sẽ gọi redefined method. Tìm hiểu thêm Class#new và Class#allocate trong Ruby documentation.

Tại sao làm điều này?

Như nhiều thứ, modify cách Ruby tạo object từ class có thể dangerous và things break unexpected ways.

Tuy nhiên, có valid use cases thay đổi object creation. Ví dụ, ActiveRecord dùng allocate với init_from_db khác để thay đổi initialization process khi tạo object từ database so với build unsaved object. Cũng dùng allocate convert records giữa single-table inheritance types với becomes.

Quan trọng nhất, chơi với object creation cho deeper insight cách nó work trong Ruby và mở mind cho different solutions. Hy vọng bạn enjoy bài viết.

Chúng tôi muốn nghe những gì bạn implement bằng thay đổi default way Ruby tạo object. Đừng ngần ngại tweet thoughts @AppSignal.