单例模式
目录
定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
本质: 控制实例数目
场景
项目中的配置文件的读取与使用例子
package main
import "fmt"
type AppConfig struct {
AppName string
AppVersion string
RunMode string
HttpPort int
}
//构造方法
func NewAppConfig() *AppConfig {
return &AppConfig{AppName: "68hub.com",AppVersion: "v1.0.0.",RunMode: "release",HttpPort: 80}
}
func main() {
appConfig := NewAppConfig()
fmt.Println(appConfig.AppName, appConfig.AppVersion, appConfig.RunMode)
}
在系统运行中有很多用到配置文件的地方,也就是说在很多地方需要创建AppConfig
实例。这样会严重浪费内存资源。事实上,对于AppConfig
这种类,在运行期间只需要一个实例化对象就可以了。
一个类能够被创建多个实例,问题的根源在于类的构造方法是公开的,也就是可以让类的外部来通过构造方法创建多个实例。换句话说,只要类的构造方法能让类的外部访问,就没有办法控制外部创建这个类的个数。 要想控制一个类只被创建一个实例,首要问题就是要把创建实例的权限收回。让类自身来负责自己类实例的创建工作,然后有这个类提供一个外部可以访问这个类实例的方法。
示例
package main
import (
"fmt"
"sync"
)
var (
Singleton *AppConfig
once sync.Once
)
type AppConfig struct {
AppName string
AppVersion string
RunMode string
HttpPort string
}
func GetInstance() *AppConfig {
once.Do(func() {
Singleton = &AppConfig{}
})
return Singleton
}
func main() {
appConfig := GetInstance()
fmt.Println(appConfig.AppName, appConfig.AppVersion, appConfig.RunMode)
}
单例模式建议命名GetInstance()
,这个方法返回的类型肯定是单例类的类型了。可以有参数,用来创建类实例所需要的参数。
懒汉式和饿汉式实现
懒汉式 (使用PHP来实现)
-
私有化构造方法
golang中
没有构造方法这个概念,类似php
中可以private function __construct(){}
-
获取实例的方法
构造方法被私有化了,外部用这个类的地方无法创建类实例,就没有办法调用这个对象方法了。所以需要让这个类提供一个方法返回类的实例,方便外面使用。
public function getInstance(){}
-
把获取类实例的方法变成静态的
客户端想要调用这个方法,需要先得到类实例,才可以调用。可是
getInstance()
方法就是为了得到类实例。以上有点死循环了。。解决这中情况只要给getInstance()
方法加上static
关键字就可以了, 这样就可以通过类直接调用这个方法,而不需要先得到类实例public static function getInstance(){}
-
定义存储实例的静态属性
方法定义好了,如果方法内部直接返回还是等于每次new 一个实例,可以在第一次创建实例的时候将其保存起来,以后可以复用这个实例。由于需要在静态方法中使用所以这个属性被迫成为一个静态属性。
private static $instance;
-
实现控制实例的创建
现在可以完善
getInstance()
内部的具体细节了,先判断是否已经创建过,即$instance
是否为空,如果不为空则直接返回,否则创建一个并赋值给$instance
并返回。(由于php-fpm
进程特性,这里不会出现并发线程安全的问题)<?php class Singleton{ private static $instance; private function __construct(){} /** * @return Singleton */ public static function getInstance(): Singleton { if (static::$instance){ return self::$instance; } static::$instance = new self(); return static::$instance; } }
饿汉式(Java语法)
Java中static
变量只会被初始化一个,就是在类装载的时候,而且多个实例都会共享这个内存空间,这恰恰满足单例模式需要的功能。具体代码如下。
public class Singleton {
//在初始化的时候就创建类实例了
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
//直接使用已经创建好的实例
return instance;
}
}
此方案用到了
java
中的static
的特性。
优缺点
-
时间和空间
懒汉式是典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断时间。当然如果一直没有人使用的话,那就不会创建实例,则节省内存空间。 饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管用不用,先创建出来,然后每次调用的时候就不需要判断了。节省运行时间。
-
线程安全
不加同步的懒汉式的线程不安全,两个线程同时并发调用就会出现问题。饿汉式是线程安全的,因为类只会被装在一次。如果给懒汉式加锁那么就是线程安全的。
总结
单例模式的懒汉式实现还体现了缓存的思想。当某些资源频繁的使用的时候,可以将其放置内存中去。 当需要控制一个类的实力只能有一个,而且客户只能从一个全局访问点访问它时,可以选用单例模式。