Bạn có bao giờ muốn tạo một class chỉ có thể có một instance?
Có? Không? Có lẽ?
Vâng…
Đó chính xác là mẫu Singleton.
Không có vấn đề gì với điều đó.
Nhưng một số người gọi đây là anti-pattern vì nó có thể được dùng làm cái cớ cho thiết kế kém.
Hãy xem ví dụ mã để bạn học cách nó hoạt động.
Ví dụ mã Mẫu Singleton
Ý tưởng của mẫu Singleton là bạn muốn một class chỉ có một instance.
Cách làm:
- Bạn làm phương thức
newtrở thành private - Bạn định nghĩa một phương thức class tên
instancetrả về instance duy nhất cho class đó
Vì đây là một mẫu thiết kế phổ biến, thư viện chuẩn Ruby đi kèm với module Singleton bạn có thể include vào bất kỳ class nào.
Nó trông như sau:
require 'singleton' class Shop include Singleton end
Bây giờ nếu bạn cố tạo đối tượng Shop với Shop.new bạn sẽ thấy:
Shop.new # NoMethodError: private method `new' called for Shop:Class
Và nếu bạn muốn lấy đối tượng Shop duy nhất bạn có thể dùng phương thức instance:
Shop.instance.object_id # 5659218 Shop.instance.object_id # 5659218
Bạn có thể thấy nó luôn là cùng một đối tượng vì object_id không bao giờ thay đổi.
Điều bạn cần nhớ:
Đây là một mẫu đơn giản, nó giới hạn việc tạo đối tượng ở 1 đối tượng & bạn có thể yêu cầu đối tượng đó bất cứ khi nào muốn.
Tại sao Bạn KHÔNG nên dùng Mẫu này
Mẫu Singleton thường là cái cớ để có某种形式 của biến toàn cục (global variable) & chúng ta biết biến toàn cục là xấu vì bạn không có sự kiểm soát đối với chúng. Điều này làm cho việc kiểm thử khó khăn hơn & có thể tăng coupling.
“Vấn đề thực sự với Singleton là chúng cho bạn một cái cớ tốt để không suy nghĩ kỹ lưỡng về độ hiển thị phù hợp của một đối tượng.” — Kent Beck
Hãy tưởng tượng có một đối tượng mà hầu hết các class của bạn phụ thuộc vào, đó là thứ mẫu Singleton có thể trở thành. Chúc may mắn khi refactor cái đó!
Bây giờ:
Có trường hợp nào tốt để dùng mẫu này không?
Tìm kiếm trên Github thấy một số repo sử dụng mẫu Singleton kết hợp với mẫu Null Object.
Tôi đoán điều đó hợp lý, Rails sử dụng điều này như một phần của ActionPack.
Đây là mã:
class AllType < Type
include Singleton
def initialize
super "*/*", :all
end
def all?; true; end
def html?; true; end
end
Đây là class kế thừa từ Type.
Khi bạn tạo đối tượng AllType qua phương thức instance, nó sẽ gọi super & trả về đối tượng.
Mỗi lần bạn gọi instance sau đó bạn sẽ nhận được cùng một đối tượng.
Kết luận:
Có, mẫu này có thể hữu ích trong đúng tình huống, nhưng bạn không muốn dùng nó mà không suy nghĩ kỹ.
Video
Tóm tắt
Bạn đã học về mẫu thiết kế singleton, cách triển khai nó trong Ruby & tại sao bạn nên tránh nó hầu hết thời gian.
Bạn nghĩ sao về mẫu này? Bạn đã thấy nó trước đây? Cho tôi biết trong bình luận 🙂
Cảm ơn bạn đã đọc!