Bài viết được sự cho phép của BQT Kinh nghiệm lập trình
Singleton Pattern là gì? Singleton là 1 trong 5 design pattern của nhóm khởi tạo (Creational Design Pattern). Vậy cấu trúc của Singleton ra sao và cách triển khai như thế nào, cùng mình tìm hiểu nội dung dưới đây.
Định nghĩa
Singleton is a creational design pattern that lets you ensure that a class has only one instance, while providing a global access point to this instance.
Single Pattern là một design pattern trong số 5 design pattern thuộc nhóm Creational Design Pattern (Theo Gang of Four patterns, một cuốn sách rất nổi tiếng về design pattern).
Xem thêm Design Pattern là gì?
Creational | Structure | Behavioral |
---|---|---|
Abstract factory | Adapter | Chain of responsibility |
Builder | Bridge | Command |
Factory | Composite | Interpreter |
Prototype | Decorator | Iterator |
#Singleton | Facade | Mediator |
Flyweight | Memento | Memento |
Proxy | Observer | |
Strategy | ||
Template Method | ||
Visitor |
Singleton là một design pattern mà: Đảm bảo rằng một class chỉ có duy nhất một instance, và cung cấp một cách để truy cấp tới instance đó.
Singleton giải quyết bài toán nào?
Singleton Pattern giải quyết 2 vấn đề dưới đây cùng 1 lúc:
- Đảm bảo rằng 1 lớp (class) sẽ chỉ có 1 instance duy nhất: Sẽ có 1 số trường hợp mà bạn cần kiểm soát việc truy cập đến các tài nguyên dùng chung ví dụ như database hay 1 file nào đó; lúc này bạn cần kiểm soát được số lượng instance mà 1 class đó có.
Cách nó hoạt động sẽ như sau: Thử tưởng tượng rằng bạn đã tạo 1 object rồi, tuy nhiên sau đó bạn lại quyết định tạo thêm 1 object mới. Lúc này thay vì việc nhận được 1 object mới thì bạn sẽ nhận về object mà bạn tạo ra lúc trước.
Lưu ý rằng hành vi này không thể thực hiện với 1 phương thức khởi tạo thông thường (như sử dụng new), vì nó sẽ luôn trả về 1 object mới. Ở đây khách hàng có thể không nhận ra rằng họ đang làm việc với cùng 1 đối tượng.
- Cung cấp 1 điểm truy cập global đến instance đó: Biến global (toàn cục) thường được sử dụng để lưu trữ 1 số đối tượng thiết yếu. Mặc dù nó rất là tiện dụng, nhưng chúng cũng rất không an toàn vì bất cứ đoạn code nào trong chương trình cũng có thể ghi đè nội dung của những biến đó khiến cho ứng dụng của chúng ta bị crash. Singleton cũng giống như biến toàn cục, nó cho phép bạn truy cập đến 1 số object ở bất kỳ đâu trong chương trình, tuy nhiên nó cũng bảo vệ instance đó tránh khỏi việc bị ghi đè bởi code khác.
Cấu trúc của Singleton Pattern
Để biến một class thành Singleton, cần đảm bảo rằng:
- Định nghĩa một attribute là private static và đó là thể hiện duy nhất của class này
- Định nghĩa public static getInstance() dùng để khởi tạo đối tượng (hàm accessor)
- Thực hiện lazy-init trong hàm accessor (chỉ khi gọi mới khởi tạo thể hiện)
- Constructor (hoặc các constructor) là private hay protected, vì bạn không muốn client tạo nhiều thể hiện
- Client chỉ có thể gọi hàm accessor khi muốn có thể hiện của class
Code ví dụ
Code ví dụ này được viết bằng Java, các ngôn ngữ khác cũng sẽ tương tự.
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
Lưu ý
Các pattern khác có thể dùng cùng Singleton. Chẳng hạn, Abstract Factory, Builder, Prototype (sẽ trình bày cụ thể trong từng bài riêng cho mỗi pattern). Các đối tượng Facade và State cũng thường là Singleton.
Không nên hiểu máy móc rằng Singleton nghĩa là tồn tại chính xác 1 thể hiện. Có thể có những thể hiện khác nhau cho những mục đích khác nhau. Đây cũng là ưu điểm của Singleton so với việc dùng biến toàn cục (global variable).
Singleton là toàn cục. Vì vậy, khi đơn giản là muốn truyền một đối tượng A cho đối tượng B xử lý, hãy cân nhắc xem bạn có thật sự cần một đối tượng toàn cục hay không. Giống như thời trang vậy, che bao nhiêu, khoe bao nhiêu, bạn phải tự tìm lấy một điểm cân bằng.
Thận trọng với đa luồng (multithreading). Hai luồng khác nhau có thể gọi phương thức khởi tạo ở cùng một thời điểm và sinh ra hai thể hiện. Trong khi đó, đồng bộ (synchronized) phương thức khởi tạo lại ảnh hưởng tới hiệu suất.
Singleton: pattern hay anti-pattern?
Singleton có phải anti-pattern hay không? Tức là, nó nên tránh hay không? Điều này còn phụ thuộc nhiều thứ và sẽ gây nhiều tranh cãi. Dưới đây là một ý kiến mà bạn nên cân nhắc.
Có bốn đại cao thủ, mà đồng đạo code lâm thường gọi là Tứ Nhân Bang (Gang of Four), gồm Erich Gamma, Richard Helm, Ralph Johnson, và John Vlissides. (Đừng nhầm với “bè lũ bốn tên” bên Tung Của nha). Ngày 21/10/1994, họ có tung vào trong giang hồ một cuốn bí kíp, gọi là “Design Patterns: Elements of Reusable Object-Orientated Software”. Nó cổ vũ người người, nhà nhà sử dụng design pattern theo một cách “có quy củ”.
Bên cạnh các chiêu thức khác, Singleton được yêu mến hết mực, thậm chí so bề tài sắc lại là phần hơn. Ấy cũng vì nó dễ hiểu ý tưởng, dễ đem vào sử dụng. Nhưng hỡi ôi, kiếm tốt rồi cũng đứt tay người. Singleton trở thành con dao hai lưỡi đâm vào sau lưng nhiều đại hiệp. Những người thiết kế hệ thống quá lạm dụng nó đã khiến họ phải nhận lời cay đắng từ các lập trình viên hì hụi refactor.
Và miệng đời gán cho Singleton cái danh ác quỷ.
Tại sao lại có lời cay nghiệt như vậy? Vì những người thiết kế phớt lờ bốn lưu ý ở trên. À quên, hồi ấy đã làm gì có mấy lưu ý đó. Họ đã làm thế này. Đầu tiên, họ ép những đối tượng có thể có nhiều thể hiện trở nên chỉ có một thể hiện. Và code bỗng không còn dễ thay đổi, dễ cập nhật nữa, không còn flexible. Và tiếp theo, tai hại hơn, họ khiến chương trình khó test, khó debug hơn bằng cách dùng chiêu thức singleton mà họ ưa thích. Rất khó để viết unit test cho những đoạn code dùng chiêu thức ấy.
Chính vì những tác hại khôn lường mà singleton có thể đem lại, chúng ta nên nghiêm túc nhìn nhận rằng: nó quả thực là một anti-pattern. Khi muốn áp dụng nó, hãy chắc chắn rằng bạn đã xem xét các lưu ý bên trên. Dấu hiệu để lựa chọn Singleton chính là: liệu việc tạo ra nhiều thể hiện của class có gây nguy hiểm hay không (chẳng hạn một công ty mà có nhiều giám đốc điều hành). Đồng thời, cũng cần đảm bảo việc dùng Singleton không ảnh hưởng đến việc thực thi, khả năng nâng cấp, bảo trì của chương trình.
Mong rằng bài viết này sẽ giúp ích cho việc lập trình của bạn. Và hãy giữ cho việc áp dụng design pattern là “healthy & balance”.
Bài viết gốc được đăng tải tại kinhnghiemlaptrinh.com
Có thể bạn quan tâm:
- Code PHP làm sao cho sạch (Phần 1)
- Repository design pattern hoàn thiện trong Laravel
- Làm thế nào để sắp xếp Clean Architecture theo Modular Patterns trong 10 phút?
Xem thêm IT Jobs Developer hấp dẫn trên TopDev