Mẫu Singleton
Thuộc phần Lập trình hướng đối tượng trong hành trình C của Coddy. Bài học 54 trên 61.
Đôi khi, bạn cần có đúng một đối tượng trong toàn bộ chương trình, chẳng hạn như trình quản lý cấu hình, hệ thống ghi nhật ký hoặc kết nối cơ sở dữ liệu. Mẫu Singleton đảm bảo chỉ có một đối tượng tồn tại và cung cấp một điểm truy cập toàn cục đến đối tượng đó.
Trong C, ta triển khai cách này bằng một biến static bên trong tệp mã nguồn. Hãy nhớ rằng static ở phạm vi tệp có nghĩa là biến chỉ dùng riêng trong tệp đó và tồn tại suốt thời gian chạy của chương trình. Kết hợp với một hàm getter, cách này tạo nên Singleton của chúng ta:
// config.c
#include "config.h"
#include <stdlib.h>
struct Config {
int max_connections;
int timeout;
};
static Config* instance = NULL;
Config* get_config(void) {
if (instance == NULL) {
instance = malloc(sizeof(Config));
instance->max_connections = 100;
instance->timeout = 30;
}
return instance;
}Điều kỳ diệu nằm ở get_config(). Lần gọi đầu tiên tạo và khởi tạo instance. Mỗi lần gọi tiếp theo đều trả về cùng một con trỏ.
Từ khóa static giúp instance giữ nguyên giá trị giữa các lần gọi hàm và ẩn với các tệp khác.
Thông qua phần đầu, người dùng chỉ thấy hàm getter. Họ không thể truy cập hoặc sửa đổi trực tiếp biến tĩnh. Dù bạn gọi get_config() bao nhiêu lần hoặc từ đâu, bạn luôn nhận được cùng một đối tượng.
Thử thách
DễHãy tạo một module Settings để quản lý cấu hình trên toàn ứng dụng bằng mẫu Singleton. Dù code của bạn yêu cầu cài đặt bao nhiêu lần, chương trình luôn trả về cùng một instance.
Bạn sẽ sắp xếp code trong ba tệp:
settings.h: Khai báo kiểuSettingskhông hiển thị cấu trúc bên trong bằng khai báo trước. Khai báo hàmget_settingstrả về con trỏ đến instance Singleton, cùng các hàm getter và setter cho hai giá trị cấu hình:volume(số nguyên) vàbrightness(số nguyên). Các chỉ thị bảo vệ khi include rất cần thiết ở đây.settings.c: Đây là nơi mọi thứ diễn ra. Định nghĩa structSettingsthực tế với các trườngvolumevàbrightness. Tạo một biến con trỏstaticđể giữ instance duy nhất (khởi tạo bằngNULL). Cài đặtget_settingssao cho lần gọi đầu tiên cấp phát và khởi tạo instance với các giá trị mặc định (volume: 50, brightness: 75), còn những lần gọi sau chỉ trả về instance hiện có. Cài đặt các hàm getter và setter để truy cập và thay đổi những trường ẩn.main.c: Chứng minh Singleton hoạt động chính xác. Đọc hai số nguyên biểu thị giá trị volume và brightness mới. Gọiget_settingshai lần, lưu kết quả vào hai biến con trỏ khác nhau. Dùng con trỏ thứ nhất để cập nhật volume và brightness bằng các giá trị đầu vào. Sau đó, dùng con trỏ thứ hai để lấy và in ra cả hai giá trị. Nếu Singleton hoạt động, các thay đổi qua một con trỏ sẽ hiển thị qua con trỏ còn lại.
Chương trình nhận hai đầu vào:
- Giá trị volume mới
- Giá trị brightness mới
In volume và brightness trên các dòng riêng sau khi thay đổi chúng qua một con trỏ và đọc qua con trỏ khác.
Đầu ra mẫu khi đầu vào là 80 và 100:
80
100Đầu ra mẫu khi đầu vào là 25 và 60:
25
60Đầu ra mẫu khi đầu vào là 0 và 50:
0
50Điểm mấu chốt là cả hai con trỏ đều trỏ đến cùng một đối tượng trong bộ nhớ: đó chính là bản chất của mẫu Singleton. Biến static trong tệp mã nguồn đảm bảo chỉ có một instance tồn tại và instance đó tồn tại suốt vòng đời của chương trình.
Tự làm thử
#include <stdio.h>
#include "settings.h"
int main() {
int new_volume, new_brightness;
scanf("%d", &new_volume);
scanf("%d", &new_brightness);
// TODO: Gọi get_settings hai lần và lưu kết quả vào hai biến con trỏ khác nhau
// TODO: Dùng con trỏ thứ nhất để cập nhật âm lượng và độ sáng bằng các giá trị đầu vào
// TODO: Dùng con trỏ thứ hai để lấy và in ra âm lượng cùng độ sáng
// (Điều này cho thấy cả hai con trỏ đều trỏ đến cùng một instance singleton)
return 0;
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Kiến thức cơ bản về lập trình mô-đun
Tệp tiêu đềInclude guardTệp mã nguồnHàm staticÔn tập: Máy tính mô-đun4Tính đóng gói
Khái niệm con trỏ mờĐịnh nghĩa struct mờHàm truy xuất và hàm gánKiểm tra tính hợp lệ trong hàm gánÔn tập: Hộp bí mật2Đối tượng và phương thức
Struct dưới dạng đối tượngCon trỏ 'Self'Tính đúng đắn của constCon trỏ và giá trịPhương thức hỗ trợÔn tập: trình quản lý Point5Dự án: Tài khoản ngân hàng đơn giản
Thiết lập dự ánTriển khai tài khoản3Vòng đời đối tượng
Mẫu hàm khởi tạoMẫu hàm hủyKhởi tạo trên ngăn xếpSao chép sâuÔn tập: lớp bao bọc chuỗi6Kế thừa bằng cách kết hợp
Nhúng structQuy tắc thành viên đầu tiênTruy cập thành viên lớp chaUpcastingÔn tập: hệ phân cấp hình dạngTự luyện tập: Trình biên dịch C online