Nội dung
Singleton là một mẫu thiết kế khởi tạo, đảm bảo rằng chỉ có một đối tượng cùng kiểu tồn tại và cung cấp một điểm truy cập duy nhất vào nó cho bất kỳ mã nào khác.
Singleton có những ưu và nhược điểm gần giống như các biến toàn cục. Mặc dù chúng siêu tiện dụng, nhưng chúng phá vỡ tính mô đun của mã của bạn.
Cách sử dụng mẫu
Sử dụng: Rất nhiều nhà phát triển coi mẫu Singleton là một mẫu phản thiết kế mẫu. Đó là lý do tại sao việc sử dụng nó đang giảm trong mã C#.
Nhận biết: Singleton có thể được nhận dạng bằng một phương thức khởi tạo tĩnh, phương thức này trả về cùng một đối tượng được lưu trong bộ nhớ cache.
Singleton thơ ngây
Khá dễ dàng để triển khai một Singleton thơ ngây. Bạn chỉ cần ẩn hàm tạo và thực hiện một phương thức tạo tĩnh.
Việc này có thể không chính xác trong môi trường đa luồng. Nhiều luồng có thể gọi phương thức khởi tạo đồng thời và nhận một số thể hiện của lớp Singleton.
Program.cs
using System;
namespace Singleton
{
// Lớp Singleton định nghĩa phương thức GetInstance dùng như một phương thức thay
// thế cho hàm tạo và cho phép các client truy cập lặp đi lặp lại cùng một thể hiện
// của lớp này.
class Singleton
{
// Phương thức khởi tạo của Singleton phải luôn ở private để ngăn các lệnh gọi
// xây dựng trực tiếp với toán tử new.
private Singleton() { }
// Thể hiện của Singleton được lưu trữ trong một trường tĩnh. Có nhiều cách để
// khởi tạo trường này, tất cả chúng đều có ưu và nhược điểm khác nhau. Trong
// ví dụ này sẽ chỉ ra những cách đơn giản nhất trong số những cách này, tuy
// nhiên, cách này không thực sự hoạt động tốt trong chương trình đa luồng.
private static Singleton _instance;
// Đây là phương thức tĩnh kiểm soát quyền truy cập vào cá thể singleton.
// Trong lần chạy đầu tiên, nó tạo một đối tượng singleton và đặt nó vào
// trường tĩnh. Trong các lần chạy tiếp theo, nó trả về đối tượng hiện
// có của client được lưu trữ trong trường tĩnh.
public static Singleton GetInstance()
{
if (_instance == null)
{
_instance = new Singleton();
}
return _instance;
}
// Cuối cùng, bất kỳ singleton nào cũng phải xác định một số logic,
// có thể được thực thi trên thể hiện của nó.
public static void someBusinessLogic()
{
// ...
}
}
class Program
{
static void Main(string[] args)
{
// The client code.
Singleton s1 = Singleton.GetInstance();
Singleton s2 = Singleton.GetInstance();
if (s1 == s2)
{
Console.WriteLine("Singleton works, both variables contain the same instance.");
}
else
{
Console.WriteLine("Singleton failed, variables contain different instances.");
}
}
}
}
Kết quả:
Singleton works, both variables contain the same instance.
Singleton trong môi trường đa luồng
Để khắc phục sự cố phải đồng bộ hóa các luồng trong lần tạo đối tượng Singleton đầu tiên.
Program.cs
using System;
using System.Threading;
namespace Singleton
{
// Lớp Singleton định nghĩa phương thức GetInstance dùng như một phương thức thay
// thế cho hàm tạo và cho phép các client truy cập lặp đi lặp lại cùng một thể hiện
// của lớp này.
class Singleton
{
private Singleton() { }
private static Singleton _instance;
// Một đối tượng khóa sẽ được sử dụng để đồng bộ hóa các luồng trong lần truy
// cập đầu tiên vào Singleton.
private static readonly object _lock = new object();
public static Singleton GetInstance(string value)
{
// Điều kiện này là cần thiết để ngăn chặn các luồng xảy ra tình trạng
// dẫm đạp khi thể hiện đã sẵn sàng.
if (_instance == null)
{
// Hãy tưởng tượng rằng khi chương trình vừa được khởi chạy. Vì chưa
// có thể hiện Singleton, nhiều luồng có thể đồng thời vượt qua điều
// kiện trước đó và đến chỗ này gần như cùng một lúc. Luồng đầu tiên
// trong số chúng sẽ nhận được khóa và sẽ tiếp tục, trong khi những
// luồng còn lại sẽ đợi ở đây.
lock (_lock)
{
// Luồng đầu tiên có được khóa, đến điều kiện này, đi vào bên
// trong và tạo thể hiện Singleton. Sau khi nó rời khỏi khối khóa,
// một luồng có thể đang chờ giải phóng khóa sau đó có thể đi
// vào phần này. Nhưng vì thể hiện đã được khởi tạo nên
// luồng sẽ không tạo đối tượng mới.
if (_instance == null)
{
_instance = new Singleton();
_instance.Value = value;
}
}
}
return _instance;
}
// Ta sẽ sử dụng thuộc tính này để kiểm tra sự hoạt động của Singleton.
public string Value { get; set; }
}
class Program
{
static void Main(string[] args)
{
// The client code.
Console.WriteLine(
"{0}\n{1}\n\n{2}\n",
"If you see the same value, then singleton was reused (yay!)",
"If you see different values, then 2 singletons were created (booo!!)",
"RESULT:"
);
Thread process1 = new Thread(() =>
{
TestSingleton("FOO");
});
Thread process2 = new Thread(() =>
{
TestSingleton("BAR");
});
process1.Start();
process2.Start();
process1.Join();
process2.Join();
}
public static void TestSingleton(string value)
{
Singleton singleton = Singleton.GetInstance(value);
Console.WriteLine(singleton.Value);
}
}
}
Kết quả:
If you see the same value, then singleton was reused (yay!)
If you see different values, then 2 singletons were created (booo!!)
RESULT:
FOO
FOO
Để lại một bình luận