目录

定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

本质: 控制实例数目

场景

项目中的配置文件的读取与使用例子

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来实现)

  1. 私有化构造方法

    golang中没有构造方法这个概念,类似php中可以

    private function __construct(){}
    
  2. 获取实例的方法

    构造方法被私有化了,外部用这个类的地方无法创建类实例,就没有办法调用这个对象方法了。所以需要让这个类提供一个方法返回类的实例,方便外面使用。

    public function getInstance(){}
    
  3. 把获取类实例的方法变成静态的

    客户端想要调用这个方法,需要先得到类实例,才可以调用。可是getInstance()方法就是为了得到类实例。以上有点死循环了。。解决这中情况只要给getInstance()方法加上static关键字就可以了, 这样就可以通过类直接调用这个方法,而不需要先得到类实例

    public static function getInstance(){}
    
  4. 定义存储实例的静态属性

    方法定义好了,如果方法内部直接返回还是等于每次new 一个实例,可以在第一次创建实例的时候将其保存起来,以后可以复用这个实例。由于需要在静态方法中使用所以这个属性被迫成为一个静态属性。

    private static $instance; 
    
  5. 实现控制实例的创建

    现在可以完善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的特性。

优缺点

  • 时间和空间

    懒汉式是典型的时间换空间,也就是每次获取实例都会进行判断,看是否需要创建实例,浪费判断时间。当然如果一直没有人使用的话,那就不会创建实例,则节省内存空间。 饿汉式是典型的空间换时间,当类装载的时候就会创建类实例,不管用不用,先创建出来,然后每次调用的时候就不需要判断了。节省运行时间。

  • 线程安全

    不加同步的懒汉式的线程不安全,两个线程同时并发调用就会出现问题。饿汉式是线程安全的,因为类只会被装在一次。如果给懒汉式加锁那么就是线程安全的。

总结

单例模式的懒汉式实现还体现了缓存的思想。当某些资源频繁的使用的时候,可以将其放置内存中去。 当需要控制一个类的实力只能有一个,而且客户只能从一个全局访问点访问它时,可以选用单例模式。