Singleton Pattern เป็น Design Pattern ที่จำกัดการสร้าง Instance ของ Class ให้มีเพียง หนึ่งเดียวตลอดชีวิตโปรแกรม ให้ Global Access Point ไปยัง Instance นั้น
Classic Implementation (Meyers Singleton - C++11 Thread-Safe)
#include <iostream>
#include <memory>
class Singleton {
private:
int data_;
// Private Constructor: ป้องกันการสร้างจากภายนอก
Singleton() : data_(0) {
std::cout << "Singleton Constructed\n";
}
// Delete Copy/Move: ป้องกัน Copy
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
Singleton(Singleton&&) = delete;
Singleton& operator=(Singleton&&) = delete;
public:
// Static Access Method: Thread-Safe Initialization (C++11 Magic Static)
static Singleton& getInstance() {
static Singleton instance; // Guaranteed Thread-Safe Init (C++11)
return instance;
}
int getData() const { return data_; }
void setData(int val) { data_ = val; }
// Optional: Destructor Control
~Singleton() { std::cout << "Singleton Destroyed\n"; }
};
int main() {
Singleton& s1 = Singleton::getInstance();
std::cout << "Data: " << s1.getData() << '\n'; // 0
s1.setData(100);
Singleton& s2 = Singleton::getInstance(); // Same Instance
std::cout << "Data: " << s2.getData() << '\n'; // 100
std::cout << "Same address? " << (&s1 == &s2) << '\n'; // 1 (true)
}
ผลลัพธ์
Singleton Constructed
Data: 0
Data: 100
Same address? 1
Singleton Destroyed
ทำไม Meyers Singleton ดีกว่า Classic Pointer Version?
| ด้าน | Classic (Static Pointer) | Meyers (Static Local) |
|---|---|---|
| Thread Safety (Init) | Manual (Mutex/Double-Check) | ✅ Automatic (C++11) |
| Destruction Order | Manual/Leak Risk | Automatic (Reverse Init Order) |
| Lazy Initialization | Manual | ✅ Automatic |
| Exception Safety | Complex | ✅ Automatic |
การใช้งานจริง: Logger, Config, Database Pool
class Logger {
std::ofstream logFile_;
Logger() : logFile_("app.log", std::ios::app) {}
public:
static Logger& instance() {
static Logger logger;
return logger;
}
void log(const std::string& msg) {
logFile_ << "[" << currentTime() << "] " << msg << '\n';
}
// Delete Copy/Move
Logger(const Logger&) = delete;
Logger& operator=(const Logger&) = delete;
};
// Usage
Logger::instance().log("Application started");
ข้อเสียและการวิพากษ์ (Anti-Pattern?)
- Global State: ยากต่อ Testing (Mocking ยาก)
- Hidden Dependencies: Class ใช้ Singleton โดยไม่ปรากฏใน Signature
- Tight Coupling
- Destruction Order Issues (ถ้ามี Multiple Singletons อ้างอิงกัน)
ทางเลือก: Dependency Injection
// แทนที่จะเรียก Singleton::getInstance() ภายใน Class
class Service {
Logger& logger_; // Injected
public:
Service(Logger& logger) : logger_(logger) {}
void doWork() { logger_.log("Working"); }
};
// Main: Wire Dependencies
int main() {
Service service(Logger::instance()); // หรือ Mock Logger สำหรับ Test
}
สรุป
Meyers Singleton (static Local Variable ใน Function) เป็น Implementation ที่ดีที่สุดใน C++11+ (Thread-Safe, Exception-Safe, Lazy) แต่ควรพิจารณา Dependency Injection แทนสำหรับ Testability และ Loose Coupling ใช้ Singleton เฉพาะ Cross-cutting Concerns (Logging, Config, Hardware Access)