Singleton Design Pattern: Implementation and Practical Applications
The Singleton design pattern guarantees a single, globally accessible instance, reducing memory overhead and global variables, with eager (init) and lazy (sync.Once) thread‑safe implementations in Go, and practical uses such as configuration management in Python, shared DB connections in PHP, and UI components like toasts in JavaScript.
To write good code, Design Pattern is an essential fundamental skill. Design Pattern provides solutions to recurring problems in Object Oriented Design. This article starts with the simplest pattern: Singleton Pattern.
Singleton Pattern belongs to the Creational Pattern category. Its purpose is to ensure that a class has only one instance and provide a global access point to it. The Singleton Pattern creates an object only once in memory, and even if the class is instantiated multiple times, it returns only the first instantiated instance. This not only reduces unnecessary memory overhead but also has significant importance in reducing global functions and variables.
There are two main implementation approaches: Lazy Singleton and Eager Singleton. In multi-threaded scenarios, thread safety must be considered.
The most common use case is global configuration management, followed by IO operations and frontend interactions where object uniqueness must be guaranteed.
01 Implementation Methods in Go
In Golang, there are multiple ways to implement Singleton Pattern. This article introduces thread-safe singleton implementation using init and sync.Once.
The init function executes once when the package is first loaded (Eager Singleton), while sync.Once executes when needed during runtime and only once (Lazy Singleton). The init function creates the object directly without checking for null and continuously occupies memory. sync.Once is a Go language construct that uses sync/atomic package atomic operations for efficient and concurrent-safe implementation. It uses a variable done to record function execution status, with sync.Mutex and sync.Atom to ensure thread-safe reading of done. Note that once.Do(f func()) cannot be nested; if f calls once.Do during execution, it will cause deadlock.
In Golang server applications, resources like DB, Redis, and Logger are frequently used. Creating and releasing these resources in each request would cause significant system overhead. Using singleton pattern avoids this problem. Typically, singleton objects are created via init or sync.Once before the HTTPServer goroutine starts in the main process.
var (
oResource sync.Once
initFuncList = []initFunc{
mustInitLoggers, // 初始化Log
mustInitServicer, // 初始化servicer以及ral
mustInitGorm, // 初始化mysql gorm
mustInitRedis, // 初始化redis
mustInitOther, // 初始化other
}
)
type initFunc func(context.Context) func() error
// MustInit 按顺序初始化app所需的各种资源
func MustInit(ctx context.Context) (f func() error) {
oResource.Do(func() {
callbackList := make([]func() error, 0, len(initFuncList))
for _, f := range initFuncList {
callbackList = append(callbackList, f(ctx))
}
f = func() (err error) {
for i := len(callbackList) - 1; i >= 0; i-- {
if callbackList[i] != nil {
e := callbackList[i]()
if err == nil {
err = e
}
}
}
return
}
})
return
}02 Application in Configuration Management (Python)
A common application in Python is using Singleton Pattern for global configuration management. Most systems have configuration files for various runtime settings. During system operation, code reads and parses these files to obtain configuration information, and when configuration files change, the corresponding information needs to be updated in real-time.
Problems with reading configuration each time:
Increased time consumption: Additional time overhead proportional to the number of reads
Increased memory: Additional memory overhead proportional to the number of object instances
Singleton Pattern is ideal for this scenario: load and parse the configuration file during system initialization or first use, create a configuration object that monitors file changes and updates properties, then use this object directly for subsequent access.
# runtime_conf.py
class RuntimeConf(object):
"""
单例模式实现的运行时全局配置类
1、用于解析配置文件并封装成对象属性方便使用
2、持续监听配置文件,发生变更后自动更新实例对象属性
"""
def __new__(cls):
if not hasattr(cls, '_instance'):
# 1、初始化实例对象
cls._instance = super(RuntimeConf, cls).__new__(cls)
# 2、加载配置文件
cls._instance.load_config()
# 3、持续开启一个新线程持续监听文件变化,文件发生变更以后更新实例属性
cls._instance.__watch_async()
return cls._instance
def __watch_async(self):
"""
私有的监听配置文件方法,如果配置文件发生变更,重新读取配置文件并加载到 self.__data 属性
:return:
"""
# 以下仅为示例思路,具体实现文件监听可复用第三方框架,例如 pyinotify
changed = False
# ......
# 如果文件发生变更,重新加载
if changed:
self.__load_config()
def __load_config(self):
"""
私有读取配置文件并加载到对象属性中
:return:
"""
# 读取配置文件并存储到self.__data属性
self.__data = {
"key1": 1,
"key2": 2
}
print("load config success")
def get(self, key):
"""
读取配置
:param key:
:return:
"""
return self.__data.get(key, None)
if __name__ == '__main__':
# 初始化两个对象,输出对象的内存地址,可以发现两个变量都是指向同一个内存地址,即是同一个对象
conf_1 = RuntimeConf()
conf_2 = RuntimeConf()
print(conf_1)
print(conf_2)
print(conf_1.get("key1"))
print(conf_2.get("key2"))03 Application in IO Operations (PHP)
In PHP, a typical application of Singleton Pattern is IO operations such as database and file operations, maintaining a global variable to manage connection instances.
In a typical PHP site with MVC structure, a single request involves multiple Model instantiations, each requiring database operations. If each Model creates a new connection, problems arise:
Resource waste: Frequent connection establishment increases network latency and CPU/memory overhead
Cannot commit transactions: Multiple SQL statements across different connections cannot complete database transactions
Singleton Pattern solves this: the singleton class has a private constructor and provides a static method for external access to its instance. The first access returns the same object each time, which maintains the database connection.
class DBHandler {
private static $instance = null; //私有实例
public $conn; //数据库连接
//私有构造函数
private function __construct() {
$this->conn = new PDO('hostname', 'account', 'password');
}
//静态方法,用于获取私有实例
public static function getInstance() {
if (self::$instance == null) {
self::$instance = new DBHandler();
}
return self::$instance;
}
public function fetch() {...}
}
class ModelA {
private $dbHandler;
public function __construct() {
$this->dbHandler = DBHandler->getInstance();
}
public function getA() {
return $this->dbHandler->fetch($sql);
}
}
class ModelB {
private $dbHandler;
public function __construct() {
$this->dbHandler = DBHandler->getInstance();
}
public function getB() {
return $this->dbHandler->fetch($sql);
}
}
$modelA = new ModelA();
$modelB = new ModelB();
$modelA->getA();
$modelB->getB();04 Application in Frontend Interaction (JavaScript)
Singleton Pattern is widely used in frontend development. Many third-party libraries and frameworks apply this pattern. For example, jQuery exposes a single instance; multiple references use the same instance, reducing global variables and avoiding variable conflicts.
Common implementation approach: Create a class with a static method to create instance objects; maintain a flag indicating whether an instance has been created; if not, create and return the instance; if created, return the reference to the first instance.
Practical application: Using Singleton Pattern to manage page dialogs (toasts), preventing multiple overlapping dialogs. Create a Toast class with a static method getInstance to return the instance. Business code can control dialog display/hide via show and hide methods, but even multiple show calls will only display one dialog because they use the same instance.
// 弹窗组件 toast.js
class Toast {
constructor() {
this._init();
}
// 私有方法,业务不允许直接调用该方法来创建弹窗
_init(){
const toast = document.createElement('div');
toast.classList.add('toast');
toast.innerHTML = '这是一个弹窗';
document.body.append(toast);
}
show() {
document.querySelector('.toast').style.display = 'block';
}
hide() {
document.querySelector('.toast').style.display = 'none';
}
// 静态方法,业务操作弹窗时不需要再实例化
static getInstance() {
if(!this.instance) {
this.instance = new Toast();
}
return this.instance;
}
}
// 在组件中把对唯一的实例对象 loginToast 的引用暴露出去
const toast = Toast.getInstance();
export default toast;
// 业务调用
import toast from './xxx/toast';
toast.show();Baidu Geek Talk
Follow us to discover more Baidu tech insights.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.