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
trueinclude 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:
3Hã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}")
endThử 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
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
1Lớp và đối tượng
Làm việc với tệpĐịnh nghĩa lớpBiến instancePhương thức và selfto_s và inspectÔn tập: thẻ thư viện4Tính đóng gói
Phương thức riêng tưPhương thức được bảo vệPhương thức thiết lập riêng tưBảo vệ trạng tháiÔn tập: máy bán hàng tự động7Tính đa hình và duck typing
Tính đa hìnhDuck typingPhương thức trừu tượngcase và lớpÔn tập: phương thức thanh toán2Phương thức truy cập và self
Phương thức đọc và ghiPhương thức gán tùy chỉnhself trong phương thứcĐịnh danh và tính bằng nhauÔn tập: Danh sách liên hệTự luyện tập: Trình biên dịch Ruby online