大话设计模式C语言之单例模式
单例模式
单例模式 (SingletonPattern) 是一种创建型设计模式,让你能够保证一个类只有一个实例,并提供一个访问该实例的全局节点。
案例代码
对象单例
单例日志类接口 logger.h
单例(Singleton)类声明了一个名为 getInstance获取实例的静态方法来返回其所属类的一个相同实例。单例的构造函数必须对客户端(Client)代码隐藏。调用获取实例方法必须是获取单例对象的唯一方式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #ifndef LOGGER_H #define LOGGER_H
typedef struct Logger Logger;
void logger_init(void);
Logger *logger_instance(void);
void logger_log(Logger *self, const char *msg);
#endif
|
单例日志类实现 logger.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
| #include <stddef.h> #include "logger.h" #include "logger_port.h"
struct Logger { unsigned char initialized; };
static struct Logger g_logger;
void logger_init(void) { if (g_logger.initialized) { return; }
g_logger.initialized = 1; }
Logger *logger_instance(void) { return &g_logger; }
void logger_log(Logger *self, const char *msg) { if (!self || !self->initialized || !msg) { return; }
logger_port_enter_critical();
logger_port_uart_write(msg); logger_port_uart_write("\r\n");
logger_port_exit_critical(); }
|
硬件抽象
抽象接口 logger_port.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| #ifndef LOGGER_PORT_H #define LOGGER_PORT_H
void logger_port_uart_init(void);
void logger_port_uart_write(const char *str);
void logger_port_enter_critical(void); void logger_port_exit_critical(void);
#endif
|
具体抽象 logger_port.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| #include <stdio.h> #include "logger_port.h"
void logger_port_uart_init(void) { }
void logger_port_uart_write(const char *str) { while (*str) { putchar(*str++); } }
void logger_port_enter_critical(void) { }
void logger_port_exit_critical(void) { }
|
测试客户端代码 main.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| #include "logger.h"
int main(void) { logger_init();
Logger *log1 = logger_instance(); Logger *log2 = logger_instance();
if (log1 == log2) { logger_log(log1, "Singleton OK"); }
logger_log(log1, "System Booted");
while (1); return 0; }
|
总结
单例模式同时解决了两个问题,所以违反了单一职责原则:
1.保证一个类只有一个实例。为什么会有人想要控制一个类所拥有的实例数量?最常见的原因是控制某些共享资源(例如数据库或文件)的访问权限。它的运作方式是这样的:如果你创建了一个对象,同时过一会儿后你决定再创建一个新对象,此时你会获得之前已创建的对象,而不是一个新对象。普通构造函数无法实现上述行为,因为构造函数的设计决定了它必须总是返回一个新对象。
2.为该实例提供一个全局访问节点。还记得你用过的那些存储重要对象的全局变量吗?它们在使用上十分方便但同时也非常不安全,因为任何代码都有可能覆盖掉那些变量的内容,从而引发程序崩溃。和全局变量一样,单例模式也允许在程序的任何地方访问特定对象。但是它可以保护该实例不被其他代码覆盖。还有你不会希望解决同一个问题的代码分散在程序各处的。因此更好的方式是将其放在同一个类中,特别是当其他代码已经依赖这个类时更应该如此。
单例模式已经变得非常流行,以至于人们会将只解决上文描述中任意一个问题的东西称为单例。