首页 > 其他分享 >单例模式

单例模式

时间:2023-08-26 17:38:32浏览次数:40  
标签:singelton 单例 GetInstance 模式 instance 实例 func

3.4.1 单例模式中的角色和职责

单例模式的标准类图如下:

单例模式_线程安全

Singleton(单例):在单例类的内部实现只生成一个实例,同时它提供一个静态的getInstance()工厂方法,让客户可以访问它的唯一实例;为了防止在外部对其实例化,将其构造函数设计为私有;在单例类内部定义了一个Singleton类型的静态对象,作为外部共享的唯一实例。


单例模式要解决的问题是:

保证一个类永远只能有一个对象,且该对象的功能依然能被其他模块使用。


3.4.2 单例模式逻辑推演实现

package main

import "fmt"

/*
三个要点:
		一是某个类只能有一个实例;
		二是它必须自行创建这个实例;
		三是它必须自行向整个系统提供这个实例。
*/

/*
	保证一个类永远只能有一个对象
*/


//1、保证这个类非公有化,外界不能通过这个类直接创建一个对象
//   那么这个类就应该变得非公有访问 类名称首字母要小写
type singelton struct {}

//2、但是还要有一个指针可以指向这个唯一对象,但是这个指针永远不能改变方向
//   Golang中没有常指针概念,所以只能通过将这个指针私有化不让外部模块访问
var instance *singelton = new(singelton)

//3、如果全部为私有化,那么外部模块将永远无法访问到这个类和对象,
//   所以需要对外提供一个方法来获取这个唯一实例对象
//   注意:这个方法是否可以定义为singelton的一个成员方法呢?
//       答案是不能,因为如果为成员方法就必须要先访问对象、再访问函数
//        但是类和对象目前都已经私有化,外界无法访问,所以这个方法一定是一个全局普通函数
func GetInstance() *singelton {
	return instance
}

func (s *singelton) SomeThing() {
	fmt.Println("单例对象的某方法")
}

func main() {
	s := GetInstance()
	s.SomeThing()
}

上面代码推演了一个单例的创建和逻辑过程,上述是单例模式中的一种,属于“饿汉式”。含义是,在初始化单例唯一指针的时候,就已经提前开辟好了一个对象,申请了内存。饿汉式的好处是,不会出现线程并发创建,导致多个单例的出现,但是缺点是如果这个单例对象在业务逻辑没有被使用,也会客观的创建一块内存对象。那么与之对应的模式叫“懒汉式”,代码如下:

package main

import "fmt"

type singelton struct {}

var instance *singelton

func GetInstance() *singelton {
	//只有首次GetInstance()方法被调用,才会生成这个单例的实例
	if instance == nil {
		instance = new(singelton)
		return instance
	}

	//接下来的GetInstance直接返回已经申请的实例即可
	return instance
}

func (s *singelton) SomeThing() {
	fmt.Println("单例对象的某方法")
}

func main() {
	s := GetInstance()
	s.SomeThing()
}


3.4.3 线程安全的单例模式实现

上面的“懒汉式”实现是非线程安全的设计方式,也就是如果多个线程或者协程同时首次调用GetInstance()方法有概率导致多个实例被创建,则违背了单例的设计初衷。那么在上面的基础上进行修改,可以利用Sync.Mutex进行加锁,保证线程安全。这种线程安全的写法,有个最大的缺点就是每次调用该方法时都需要进行锁操作,在性能上相对不高效,具体的实现改进如下:

package main

import (
	"fmt"
	"sync"
)

//定义锁
var lock sync.Mutex

type singelton struct {}

var instance *singelton

func GetInstance() *singelton {
	//为了线程安全,增加互斥
	lock.Lock()
	defer lock.Unlock()

	if instance == nil {
		return new(singelton)
	} else {
		return instance
	}
}

func (s *singelton) SomeThing() {
	fmt.Println("单例对象的某方法")
}


func main() {
	s := GetInstance()
	s.SomeThing()
}

上面代码虽然解决了线程安全,但是每次调用GetInstance()都要加锁会极大影响性能。所以接下来可以借助"sync/atomic"来进行内存的状态存留来做互斥。atomic就可以自动加载和设置标记,代码如下:

package main

import (
	"fmt"
	"sync"
	"sync/atomic"
)

//标记
var initialized uint32
var lock sync.Mutex

type singelton struct {}

var instance *singelton

func GetInstance() *singelton {
	//如果标记为被设置,直接返回,不加锁
	if atomic.LoadUint32(&initialized) == 1 {
		return instance
	}

	//如果没有,则加锁申请
	lock.Lock()
	defer lock.Unlock()

	if initialized == 0 {
		instance = new(singelton)
		//设置标记位
		atomic.StoreUint32(&initialized, 1)
	}

	return instance
}

func (s *singelton) SomeThing() {
	fmt.Println("单例对象的某方法")
}

func main() {
	s := GetInstance()
	s.SomeThing()
}

上述的实现其实Golang有个方法已经帮助开发者实现完成,就是Once模块,来看下Once.Do()方法的源代码:

func (o *Once) Do(f func()) {   //判断是否执行过该方法,如果执行过则不执行
    if atomic.LoadUint32(&o.done) == 1 {
        return
    }
    // Slow-path.
    o.m.Lock()
    defer o.m.Unlock()  
    if o.done == 0 {
        defer atomic.StoreUint32(&o.done, 1)
        f()
    }
}

所以完全可以借助Once来实现单例模式的实现,优化的代码如下:

package main

import (
	"fmt"
	"sync"
)

var once sync.Once

type singelton struct {}

var instance *singelton

func GetInstance() *singelton {

	once.Do(func(){
		instance = new(singelton)
	})

	return instance
}

func (s *singelton) SomeThing() {
	fmt.Println("单例对象的某方法")
}

func main() {
	s := GetInstance()
	s.SomeThing()
}


3.4.4 单例模式的优缺点

优点:

(1) 单例模式提供了对唯一实例的受控访问。

(2) 节约系统资源。由于在系统内存中只存在一个对象。


缺点:

(1) 扩展略难。单例模式中没有抽象层。

(2) 单例类的职责过重。


3.4.5 适用场景

(1) 系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象。

(2) 客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。

标签:singelton,单例,GetInstance,模式,instance,实例,func
From: https://blog.51cto.com/u_11382995/7245121

相关文章

  • 设计模式-命令模式
    命令模式模式定义命令模式是一种行为定义模式,可以将请求转换成一个与请求相关的,包含该请求所有信息的独立对象,并且能够根据不同请求将方法参数化,延迟请求执行或者将其放入到队列中且能实现撤销等操作模式动机敏捷开发的原则要求,不要在代码上添加基于猜测的,实际上不需要的功......
  • 磁盘配置的3种模式
    磁盘置备的3种模式,工作中在VMwareEsxi上虚拟服务器配置虚拟硬件、威联通NAS磁盘配置上遇到过。下面介绍一下这3种模式 所谓磁盘置备,就是磁盘空间分配的技术。精简置备厚置备(延迟置零):假如说新分配的硬盘上有数据存在,配置完后硬盘上有新数据产生时,新数据会逐渐覆盖硬盘上的旧......
  • 在线教育知识付费系统源码,解码不同模式
    在当今社会,知识付费如风一般掀起了热潮,成为了许多人关注的焦点。但对于知识付费究竟是什么,有些人可能还不太了解。让我用一条短视频来为你解开这个谜题。 首先,我们有一种模式,被戏称为“贩卖焦虑”。这种模式常常针对低认知人群,以制造焦虑的方式吸引他们购买知识产品。这或许不......
  • 观察者模式-21
    概述观察者模式(ObserverPattern)又称发布-订阅(Publish/Subscribe)模式、模型视图(Model/View)模式、源监听器(Source/Listener)模式、从属者(Dependents)模式。它定义了对象之间的一对多的关系,使得一个对象的状态发生改变时,会通知相关的其他对象并根据变化的状态更新这些对象。优点:实现......
  • 策略模式在项目设计中用得最多
    日常Coding过程中,设计模式三板斧:模版、构建者、策略,今天来说下第三板斧策略设计模式。策略模式还是比较简单并且使用较多的,平常我们多运用策略模式用来消除if-else、switch等多重判断的代码,消除if-else、switch多重判断可以有效应对代码的复杂性。如果分支判断会不断变化(......
  • 大型电商网站:第一章:主要电商模式
    七大电商模式B2B–企业对企业B2B(BusinesstoBusiness)是指进行电子商务交易的供需双方都是商家(或企业、公司),她(他)们使用了互联网的技术或各种商务网络平台,完成商务交易的过程。电子商务是现代B2Bmarketing的一种具体主要的表现形式。案例:阿里巴巴C2C–个人对个人C2C即Custo......
  • 业务架构模式的演进(单体架构、垂直架构、SOA架构、微服务架构)
    引子软件架构从最初的单体架构,到垂直架构,到SOA架构,再到现在流行的微服务架构,一直处在演进与发展中。演进的过程本质上是在不停的满足愈发复杂的业务需求,因此笔者更倾向称呼它们为“业务架构”。每一次架构的演进都是基于原有架构的特性再结合实际的业务场景而进行的改进,但这并不意......
  • ios开发之 -- 单例类
    单例模式是一种软件设计模式,再它的核心结构中指包含一个被称为单例类的特殊类。通过单例模式可以保证系统中一个类只有一个势力而且该势力易于外界访问,从而方便对势力个数的控制并节约系统资源。如果希望在系统中某各类的对象只能存在一个,单例模式是最好的解决方案。单例模式目前用......
  • Opengl图形几何变换的模式
    当我们对一个图形进行一个较为复杂的变换时,我们并不直接去计算这个变换,而是将其分解为多个基本变换,再依次作用于图形。对于复合变换:$$ P^{,}=M_{n}\cdotsM_{3}\bulletM_{2}\bulletM_{1}\bulletP$$先作用的变换放在连乘式右边,后作用的变换放在连乘式左边。由于矩阵乘......
  • C++单例模式
    单例模式什么是单例模式:只能实例化一个类对象(全局有且只有一个类的static实例)使用场景:进程管理器、日志管理器、网站访问计数器、应用配置程序、线程池、服务器的连接管理器实现单例模式的原理/步骤1、禁止在类外随意实例化对象,把构造函数/拷贝构造都私有化private2、确保......