Thứ Bảy, 5 tháng 9, 2015

Singleton pattern

Trong thực tế, có rất nhiều đối tượng chúng ta chỉ cần duy nhất một thể hiện của chúng như: thread pools, caches, ghi log...Nếu có quá nhiều đối tượng này, có thể sẽ là nguyên nhân gây ra lỗi trong ứng dụng của chúng ta.
Hãy tưởng tượng Singleton pattern giống như tờ bookmark khi đọc sách. Tờ bookmark có thể đặt ở bất cứ đâu trong quyển sách và mỗi quyển sách chỉ nên có 1 bookmark. Nếu chúng ta có nhiều hơn 1 bookmark thì sẽ có khả năng gây nhầm lẫn cho chúng ta khi chúng ta ko biết được chúng ta đang đọc đến đoạn nào của cuốn sách. 
Singleton pattern đảm bảo chỉ một và duy nhất một đối tượng được khởi tạo. Singleton pattern cho ta cách truy vấn toàn cục giống như biến toàn cục (mà không có những điểm bất lợi giống biến toàn cục). Nếu dùng biến toàn cục thì bạn sẽ phải khởi tạo đối tượng ngay khi ứng dụng khởi động. Còn đối với Singleton, đối tượng sẽ được khởi tạo chỉ khi nào cần.
Đối tượng dùng Singleton thì sẽ không có public contructor, contructor sẽ là private.
Những điểm bất lợi của biến toàn cục so với singleton:
- khó biết được giá trị hiện tại của biến vì nó được set ở mọi nơi, điều gì xảy ra nếu có nhiều method set, get đồng thời giá trị của biến.
- phải hiểu một khối lượng code lớn tương tác với biến thì mới hiểu được đoạn code hiện tại đang xem xét mà có sự tương tác với biến toàn cụ

Chúng ta bắt đầu với cách thứ nhất
using System;

public class Singleton
{
   private static Singleton instance;

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null)
         {
            instance = new Singleton();
         }
         return instance;
      }
   }
}
Theo cách làm trên thì trong môi trường multi-thread, tại một thời điểm có thể có 2 thread cùng chạy vào đoạn kiểm tra biến instance == null và nó sẽ tạo ra 2 thể hiện của class. Như vậy cách này không đảm bảo Singleton trong môi trường multi-thread.
Cách 2: Đảm bảo tính thread-safe của class
public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object syncRoot = new Object();

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         lock (syncRoot) 
         {
            if (instance == null) 
               instance = new Singleton();
         }

         return instance;
      }
   }
}
Đoạn code trên sử dụng một shared object để lock phần code kiểm tra instance == null và đảm bảo chỉ có duy nhất 1 thread được phép khởi tạo instance. Cách làm này đảm bảo tính thread-safe của class Singleton nhưng lại gây ảnh hưởng về mặt hiệu năng của chương trình. Như các bạn thấy, mỗi lần gọi đến Instance là hàm lock cũng được gọi. Vì thế ta có cách dưới đây:
using System;

public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object syncRoot = new Object();

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null) 
         {
            lock (syncRoot) 
            {
               if (instance == null) 
                  instance = new Singleton();
            }
         }

         return instance;
      }
   }
}
Bạn có thể thấy cách thứ 3 này đảm bảo tính thread-safe của class Singleton mà không phải gọi lại hàm lock mỗi lần sử dụng. Đây cũng là cách mà Microsoft khuyên bạn nên sử dụng khi thiết kế một Singleton class.

Không có nhận xét nào:

Đăng nhận xét