Search This Blog

Sunday, July 3, 2016

Design Patterns trong các dự án thực tế - Singleton Pattern

Lời nói đầu

Trước khi tìm hiểu về Singleton Pattern, hãy đọc lại bài viết Design patterns là gì nếu bạn vẫn chưa biết design patterns là gì. Ngoài ra để giúp bạn dễ dàng nắm bắt nội dung bài viết, hãy chắc là bạn đã biết về Lập trình hướng đối tượng (OOP) và các tính chất, khái niệm của nó như bao đóng (encapsulation), trừu tượng (abstract), kế thừa (inheritance), đa hình (polymorphism), đối tượng (object), lớp (class), giao diện (interface)...
***
Singleton Pattern là gì?

Singleton Pattern là một trong những pattern đơn giản nhất và thuộc creational pattern (là các dạng pattern tốt nhất dùng để tạo ra đối tượng). 

Singleton Pattern có thể hiểu là chúng ta có một class mà đảm bảo chỉ có duy nhất một đối tượng của nó được tạo ra, đồng thời sẽ cung cấp một cách thức để truy cập mà không phải khởi tạo đối tượng.


Áp dụng Singleton Pattern

Đôi khi chúng ta gặp một vài yêu cầu tạo ra những chức năng cụ thể mà có thể sử dụng thường xuyên trên toàn bộ hệ thống và bạn không muốn cứ phải khời tạo đối tượng liên tục, hoặc là bạn muốn lưu giữ lại các giá trị, trạng thái của nó. Đồng thời cũng dễ thấy là nó cũng sẽ giúp ta giảm số lượng dòng code dư thừa.

Các bạn có thể dễ dàng liên tưởng đến việc tạo ra các static Utility class, nhưng Singleton Pattern classđiểm khác biệt là nó sẽ có những khai báo, xử lí của riêng nó chứ không chỉ đơn thuần là xây dựng các method dùng chung.

Hãy lấy ví dụ hệ thống của chúng ta cần xây dựng chức năng ghi log. Chức năng này sẽ được cấu hình (path, filename...), khởi tạo và ghi nội dung ra file. Ta có thể áp dụng Singleton Pattern vào chức năng này vì dễ dàng nhận ra nó là một dạng Ultility class, được sử dụng khá thường xuyên, và đồng thời cũng có các khai báo, khởi tạo đối tượng của riêng nó.

Chúng ta tạo một seal class Logger như đoạn code bên dưới:
 namespace SingletonPattern  
 {  
   public sealed class Logger  
   {  
     /// <summary>  
     /// thread-safety variable  
     /// </summary>  
     private static readonly object lockObject = new object();  

     private static Logger instance = null;  

     private string filePath = string.Empty;  

     Logger()   
     {  
       Configure();  
     }  

     /// <summary>  
     /// Single instance  
     /// </summary>  
     public static Logger Instance  
     {  
       get  
       {  
         //implement simple thread-safety  
         lock (lockObject)  
         {  
           if (instance == null)  
           {  
             instance = new Logger();  
           }  
           return instance;  
         }  
       }  
     }  

     /// <summary>  
     /// Configure the log file  
     /// </summary>  
     private void Configure()  
     {  
       this.filePath = @"C:\Users\kien\AppData\log.txt";  
     }  

     /// <summary>  
     /// Write message to log file  
     /// </summary>  
     public bool WriteLog(string message)  
     {  
       bool isOK = false;  
       try  
       {  
         using (FileStream fileStream = new FileStream(this.filePath, FileMode.Append, FileAccess.Write))  
         {  
           if (fileStream != null)  
           {  
             using (StreamWriter sw = new StreamWriter(fileStream))  
             {  
               sw.WriteLine(message);  
               sw.Close();  
               isOK = true;  
             }  
             fileStream.Close();  
           }  
         }  
       }  
       catch  
       {  
         isOK = false;  
       }  
       return isOK;  
     }  
   }  
 }  

Hãy nhìn xem class Logger của chúng ta có gì:


Đầu tiên là ta khai báo seal class. Điều này chỉ ra rằng class Logger của chúng ta sẽ không cho các class khác được phép kế thừa.
 public sealed class Logger  

Chúng ta cũng khai báo một private static readonly object để thực thi một threat-safety
 /// <summary>  
 /// thread-safety variable  
 /// </summary>  
 private static readonly object lockObject = new object();  

Chúng ta cũng muốn cấu hình cho file Logger
Logger()    
{   
    Configure();   
}   

Đây là phần quan trọng nhất của class Logger là tạo một access point.
- Trước tiên chúng ta khai báo một private instance của class Logger.
private static Logger instance = null;  

- Tiếp theo chúng ta khởi tạo một static property Instance có kiểu trả về là Logger. Bên trong chúng ta sẽ thực thi cơ chế threat-safety. Đồng thời chỉ khởi tạo duy nhất một lần biến private instance và trả về instance của nó.
/// <summary>  
/// Single instance  
/// </summary>  
public static Logger Instance  
{  
    get  
    {  
         //implement simple thread-safety  
         lock (lockObject)  
         {  
           if (instance == null)  
           {  
                instance = new Logger();
           }  
           return instance;  
         }  
     }  
}  

Cuối cùng là chúng ta xây dựng các method xử lí bao gồm cấu hình Path, ghi message ra file...
     /// <summary>  
     /// Write message to log file  
     /// </summary>  
     public bool WriteLog(string message)  
     {  
       bool isOK = false;  
       try  
       {  
         using (FileStream fileStream = new FileStream(this.filePath, FileMode.Append, FileAccess.Write))  
         {  
           if (fileStream != null)  
           {  
             using (StreamWriter sw = new StreamWriter(fileStream))  
             {  
               sw.WriteLine(message);  
               sw.Close();  
               isOK = true;  
             }  
             fileStream.Close();  
           }  
         }  
       }  
       catch  
       {  
         isOK = false;  
       }  
       return isOK;  
     }  

Đoạn code dưới đây là cách mà chúng ta sử dụng class Logger. Mỗi khi cần ghi log, chúng ta không trực tiếp khởi tạo đối tượng, không cần phải cấu hình.v..v mà sẽ truy xuất thông qua Instance của nó.
   public partial class Form1 : Form  
   {  
     public Form1()  
     {  
       InitializeComponent();  
       this.Load += Form1_Load;  
       this.FormClosing += Form1_FormClosing;  
     }  
     void Form1_FormClosing(object sender, FormClosingEventArgs e)  
     {  
       Logger.Instance.WriteLog("Form1_FormClosing");  
     }  
     void Form1_Load(object sender, EventArgs e)  
     {  
       Logger.Instance.WriteLog("Form1_Load");  
     }  
   }  


Trên đây là một ví dụ đơn giản để giới thiệu đến các bạn Singleton Pattern, phân biệt nó với static class Utility cũng như là áp dụng nó như thế nào. 

Nếu các bạn có gì thắc mắc hay muốn trao đổi thì có thể để lại comment bên dưới bài viết này. Cám ơn các bạn đã đọc bài

Share to be shared!


Bài trước - Factory Pattern

No comments:

Post a Comment