12/08/2018, 16:21

Design Patterns in Ruby: Singleton

Đây là bài viết trong chuỗi bài viết về mẫu thiết kế trong phần mềm và áp dụng của chúng như thế nào vào Ruby. Mẫu thiết kế đầu tiên được giới thiệu là Observer Pattern. Phần trước là là bài viết giới thiệu về Observer và sử dụng chúng trong ruby, bạn có thể xem tại đây observer. Trong bài viết này ...

Đây là bài viết trong chuỗi bài viết về mẫu thiết kế trong phần mềm và áp dụng của chúng như thế nào vào Ruby. Mẫu thiết kế đầu tiên được giới thiệu là Observer Pattern. Phần trước là là bài viết giới thiệu về Observer và sử dụng chúng trong ruby, bạn có thể xem tại đây observer. Trong bài viết này sẽ tiếp tục sẽ nói đến một mẫu thiết kế phổ biến Singleton Pattern. Khái niệm của Singleton pattern rất đơn giản: chỉ một thực thể của một lớp được tồn tại. Điều này sẽ giúp cho một ứng dụng chỉ cho phép một đối tượng được khởi tạo với lớp tương ứng sử dụng singleton pattern. Singleton thường được sử dụng trong những ngôn ngữ khác như Java và C-based, trong Ruby thì Singleton module được chuẩn hóa thư viện và có thể thi hành một cách dễ dàng. Ví dụ chúng ta cần thiết kế một lớp để lưu giữ dữ liệu cài đặt cho một ứng dụng, và sẽ chỉ có một thực thể của lớp này tồn tại để thực hiện lưu trữ đó. Do đó chúng ta có thể sử dụng mô hình của Singleton bằng cách tạo một module, nhưng chúng ta liệu có chắc được có thể tồn tại một bản sao khác, nếu tồn tại thì sẽ mất đi mục đích của chúng ta trước đó. Bước đầu tiên là tạo một lớp Singleton, require và include module Singleton vào trong lớp đó

require 'singleton'

class AppConfig
  include Singleton
end

Nếu bạn cố gắng khởi tạo đối tượng của lớp này như các lớp thông thường, một exception NoMethodError sẽ được trả về. Hàm khởi tạo của lớp được khai báo trong private để tránh cho việc khởi tạo một đối tượng xảy ra.

AppConfig.new

#=> NoMethodError: private method `new' called for AppConfig:Class

Để truy cập vào thực thể của lớp này, chúng ta cần sử dụng phương thức instance() được cung cấp bởi Singleton module. Khi gọi tới phương thức này lần đầu tiên, một thực thể của lớp sẽ được tạo ra, và tất cả các lời gọi sau đó đến phương thức này sẽ trả về đối tượng đã được tạo ra trước đó. Bạn có thể thử như sau để biết

first, second = AppConfig.instance, AppConfig.instance
first == second

#=> true

Bây giờ chúng ta có thể biết nó hoạt động như sao, hãy sửa đổi lớp AppConfig và thêm một vài thứ như sau

#...

 attr_accessor :data

 def version
   '1.0.0'
 end

Bây giờ chúng ta thêm một thuộc tính data, nó sẽ lưu thông tin về cài đặt cấu hình, và một phương thức version trả về phiên bản hiện tại của ứng dụng. Đặt chúng cùng nhau và đây là toàn bộ class của chúng ta

require 'singleton'

class AppConfig
 include Singleton
 attr_accessor :data

 def version
   '1.0.0'
 end
end

Đơn giản như vậy là chúng ta đã thực thi xong một mẫu thiết kế là Singleton. Bây giờ chúng ta có thể sử dụng chúng ví dụ như sau

AppConfig.instance.data = {enabled: true}
=> {:enabled=>true}
AppConfig.instance.version
=> "1.0.0"

second = AppConfig.instance
second.data = {enabled: false}
=> {:enabled=>false}
AppConfig.instance.data
=> {:enabled=>false}

Đầu tiên chúng ta gán thuộc tính data với một giá trị bất kỳ và kiểm tra phiên bản của nó. Tiếp theo chúng ta sao chép thực thể singleton đó, thay đổi giá trị của data, và chúng ta sẽ thấy được toàn bộ sự thay đổi sẽ thay đổi trên một đối tượng duy nhất

Kết luận

Như vậy chúng ta có thể biết căn bản thêm về sử dụng một mẫu thiết kế nữa. Hi vọng nó sẽ giúp chúng ta nhiều khi viết code và refactor code.

References

Design Patterns in Ruby: Observer, Singleton

0