Menu
CoddyTech

Mẫu Singleton

Thuộc phần Lập trình hướng đối tượng trong hành trình Ruby của Coddy. Bài học 42 trên 57.

Mẫu thiết kế là giải pháp đã được chứng minh cho một vấn đề thiết kế phổ biến. Mẫu singleton đảm bảo một lớp chỉ có đúng một instance, có thể truy cập từ bất cứ đâu. Thư viện chuẩn của Ruby tích hợp sẵn mẫu này: require 'singleton' và include Singleton thêm phương thức lớp instance:

require 'singleton'

class Settings
  include Singleton
  attr_accessor :theme

  def initialize
    @theme = "light"
  end
end

Settings.instance.theme = "dark"
puts Settings.instance.theme
puts Settings.instance.equal?(Settings.instance)

Đầu ra:

dark
true

include Singleton cũng làm cho new trở thành phương thức riêng tư, nên không thể tạo thêm phiên bản nào. Mọi phần của chương trình gọi instance đều nhận được cùng một đối tượng:

require 'singleton'

class Counter
  include Singleton
  attr_reader :hits

  def initialize
    @hits = 0
  end

  def hit
    @hits += 1
  end
end

def page_a
  Counter.instance.hit
end

def page_b
  2.times { Counter.instance.hit }
end

page_a
page_b
puts Counter.instance.hits
begin
  Counter.new
rescue NoMethodError => e
  puts e.message
end

Đầu ra:

3
private method `new' called for class Counter

Ý tưởng tương tự khi tự viết là một phương thức lớp tạo đối tượng vào lần đầu tiên được dùng và một new riêng tư. Điều này cho thấy module làm gì giúp bạn:

class Config
  def self.instance
    @instance ||= new
  end
  private_class_method :new

  attr_accessor :level

  def initialize
    @level = 1
  end
end

Config.instance.level = 3
puts Config.instance.level

Đầu ra:

3

Hãy dùng singleton một cách hạn chế. Singleton là trạng thái toàn cục: bất kỳ đoạn code nào cũng có thể thay đổi nó, và các test không thể bắt đầu từ một bản sao sạch. Singleton phù hợp với những thứ thực sự chỉ tồn tại một lần trong mỗi chương trình, chẳng hạn như logger hoặc cấu hình; truyền một đối tượng làm tham số thường rõ ràng hơn:

# Dễ kiểm thử hơn: logger được truyền vào
def process(order, log)
  log.write("processing #{order.id}")
end
challenge icon

Thử thách

Dễ

Hoàn thiện singleton AppLogger bằng include Singleton. Nó lưu một mảng các mục và có log(message, level), phương thức này lưu [INFO] message hoặc [ERROR] message (cấp độ được viết hoa), entries và error_count. Hai hàm trợ giúp handle_info(message) và handle_error(message) trong functions.rb đều ghi nhật ký thông qua AppLogger.instance, nên chúng truy cập cùng một đối tượng.

Code được cung cấp đọc các dòng info,message hoặc error,message, chuyển chúng qua các hàm trợ giúp, in ra mọi mục và errors: 1, đồng thời kiểm tra xem AppLogger.new có bị từ chối hay không.

Code của bạn nằm trong app_logger.rb và functions.rb. main.rb chứa code đầu vào/đầu ra được cung cấp và bạn không thể chỉnh sửa tệp này.

Tự làm thử

require_relative 'app_logger'
require_relative 'functions'

# Đoạn code đầu vào/đầu ra đã cho: giữ nguyên
input = []
while (line = gets)
  input << line.chomp
end
input.each do |line|
  level, message = line.split(",", 2)
  level == "error" ? handle_error(message) : handle_info(message)
end
AppLogger.instance.entries.each { |e| puts e }
puts "errors: #{AppLogger.instance.error_count}"
begin
  AppLogger.new
  puts "new is allowed"
rescue NoMethodError
  puts "new is private"
end
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch Ruby online