Những ứng dụng thực tế của Singleton class trong Ruby

Hầu hết các Ruby developer đều đã trải qua khái niệm metaclasses hoặc singleton. Tuy nhiên khái niệm này dường như là một điều mơ hồ và hiếm khi được sử dụng, mặc dù chúng ta đều hưởng lợi từ các singleton class mỗi ngày.
Bạn có thể đã đọc các bài báo cổ điển về các loại metaclass của Ruby bởi các nhà ruby developer nổi tiếng hoặc có thể là một số bài viết thu thập các kiến thức hay. Đọc chúng có thể sẽ thấy rất lạ với các hàm có dạng class << self; self; end, nó làm bạn cảm thấy ruby có gì đó không đúng lắm. Tuy nhiên, bạn đã học được rằng có thể định nghĩa các phương thức trên một object(đối tượng) duy nhất, không có sẵn cho những object khác trong cùng một class:

matz = Object.new
def matz.speak
  "Place your burden to machine's shoulders"
end

Đó là một ví dụ cơ bản mà mình muốn nói đến hôm nay, không phải bạn không bao giờ nghĩ đến nó, chỉ là bạn làm theo nó và chưa nghĩ ra đó là một ứng dụng khá hay của singleton class trong ruby thôi.

Những ứng dụng thực tế của Singleton class

Kể từ Ruby 1.9.2, mọi object đều có một method #singleton_class, và đó là cách mình sẽ gọi nó cho phần còn lại của bài viết này. Sự gần gũi với Singleton Pattern không phải là sự trùng hợp ngẫu nhiên, các singleton class có một "instance" chính xác.
Chúng ta biết rằng các singleton class nào đó ngồi giữa một object và class của nó, tinh chỉnh các hành vi được định nghĩa trong class chỉ cho trường hợp này. Trong hầu hết các dự án, tạo ra các class khác nhau cho các object là các tricks khác nhau. Có thể không cần những singleton class này.
Mình quên một chi tiết nhỏ. Bạn có thể đã nghe nói rằng tất cả các class Ruby cũng là các object, thừa hưởng từ một class được gọi là Class. Xem, ví dụ, tại code của ActiveRecord sau đây:

User.find(42)

Tất nhiên, chúng ta tìm người dùng có id 42 từ cơ sở dữ liệu. Chúng ta làm điều đó mỗi ngày. Nhưng hãy nhìn kỹ hơn, method .find() Ở đâu?. Chúng ta không cần phải biết chính xác, nhưng chắc chắn ở đâu đó xung quanh ActiveRecord :: Base. Tổng kết lại: Các method của một object được định nghĩa trong class của nó, các class là các đối tượng, lớp của mỗi Class là Class. Khi gọi User.find, Ruby nên tìm kiếm phương thức tìm trong lớp User, mà sẽ xảy ra là Class - nhưng Class không xác định bất cứ method .find() nào. Một cái gì đó bị thiếu ở đây . . . .
Bây giờ bạn sẽ đoán được: .find() được định nghĩa trong singleton class của ActiveRecord :: Base! Đây chính xác là trường hợp sử dụng cho các singleton class: mặc dù mỗi class được thừa kế trực tiếp từ Class, mỗi class đều có các method riêng, độc lập với các class khác.

What we commonly call class methods, technically are (singleton) instance methods of class objects, defined in their respective singleton classes.

Hơn nữa, các singleton class cũng có cùng phân cấp thừa kế như các class thông thường. Singleton class của class User kế thừa từ Singleton class của ActiveRecord :: Base. Đây là một điều tuyệt vời! Thông qua đó,method .find() có sẵn trên các object của class User, cho phép chúng ta thực hiện các truy vấn trên bảng User, mặc dù việc thực hiện trong ActiveRecord :: Base không bao giờ biết gì về bảng đó.!

Class 2 cấp trong ruby

Chúng ta thấy rằng mỗi class trong Ruby đều có một singleton class tương ứng (chứa các class method được gọi là Class). Cả hai đều làm theo cùng một dòng thừa kế, và cả hai đều là trường hợp của Class (Đi chơi xung quanh mình trong IRB với #class, #singleton_class#superclass để tìm ra cách mô hình tiếp tục từ đó).

Hệ thống phân cấp 2 lớp này nghĩa là bạn có thể tận dụng các nguồn lực của kế thừa cho class_method cũng tương tự đối với instance_method, nó giống như method overriding hoặc tương tự việc gọi super. Các class_method chung như ActiveRecord :: Base.find có thể được thực hiện trong các lớp siêu trừu tượng, trả lại các kết quả cụ thể cho các lớp cụ thể mà phương pháp này cuối cùng được gọi vào

Một ví dụ khác được hưởng lợi từ hệ thống cấp bậc hai cấp này, các class có thể hành động trực tiếp như các factories riêng của họ:

class Button
  attr_accessor :label
 
  def self.create(label)
    new.tap { |button| button.label = label }
  end
end
 
class WinButton < Button; end
class NixButton < Button; end
 
class Dialog
  def initalize(factory)
    @ok_button = factory.create('OK')
    @cancel_button = factory.create('Cancel')
  end
end
 
factory = /mswin/ =~ RUBY_PLATFORM ? WinButton : NixButton
Dialog.new(factory).show

Class Dialog không cần phải biết gì về phần điều hành của hệ thống. Nó chỉ đơn giản có thể tạo ra một trường hợp mới dựa trên class Button nó được truyền vào, bởi vì .create được thừa kế (như là .new).

Tổng kết

Bởi vì toàn bộ khái niệmsingleton_class xuất phát từ Object, nó không chỉ áp dụng cho các class mà còn cho các object thông thường. Điều này cho phép các ví dụ matz.speak ban đầu và được hiển thị ở góc dưới bên trái trong sơ đồ ở trên. So với ý nghĩa của các singleton_class đối với class_method, đây chỉ là một phản ứng phụ.

Phần tìm hiểu của mình về singleton_class còn khá là sơ sài và có thể mình cũng chưa hiểu chính xác. Mong mọi người góp ý để bài viết trở nên hữu ích hơn nữa.

Cảm ơn sự quan tâm của mọi người dành cho bài viết.