Go语言 在包中使用单例的设计模式

tez616oj  于 2023-09-28  发布在  Go
关注(0)|答案(4)|浏览(90)

我仍然不是Golang的Maven,我试图理解和学习最佳实践。
如何初始化包中的单例并使其线程安全,因此即使包在不同文件中多次导入,也只有一个示例
现在示例sessions在主进程中初始化了,但之后我必须传递它。如何在包sess中初始化示例sessions,使其成为包中的全局变量或其他变量?
我希望它是sess包中的私有变量

package main

func main(){
    sessions := sess.Init()
}

sess模块

package sess

import (
    "sync"
    "time"
)

type pool struct {
    lock        sync.RWMutex
    sessions    map[string]*session
}

func Init() *pool {
    p := &pool{
        sessions:   map[string]*session{},
    }
        
    return p
}

func (p *pool) Set(sid string, s *session){
    p.lock.Lock()
    defer p.lock.Unlock()
    
    p.sessions[sid] = s
}

func (p *pool) Get(sid string) (*session, bool){
    p.lock.RLock()
    defer p.lock.RUnlock()
    
    s, ok := p.sessions[sid]
    return s, ok
}
3wabscal

3wabscal1#

使用单例声明并初始化导出的包级变量。每个包级变量都有一个初始化的示例,而不是每个导入都有一个示例或初始化。

package sess

import (
    "sync"
    "time"
)

type pool struct {
    lock        sync.RWMutex
    sessions    map[string]*session
}

var Pool = &pool{sessions: map[string]*session{}}

func (p *pool) Set(sid string, s *session){
    p.lock.Lock()
    defer p.lock.Unlock()
    
    p.sessions[sid] = s
}

func (p *pool) Get(sid string) (*session, bool){
    p.lock.RLock()
    defer p.lock.RUnlock()
    
    s, ok := p.sessions[sid]
    return s, ok
}
vhmi4jdf

vhmi4jdf2#

如果其他包依赖于pool,你可以直接注入它。例如(Go playground link):

package main

import (
    "fmt"
    "sync"
)

// Interfaces.
// As Go implements duck-typing they can be located in separate package to avoid circular dependencies
type Session interface {
    ID() uint64
    Username() string
}

type SessionPool interface {
    Set(sid string, s Session)
    Get(sid string) (Session, bool)
}

// structs

// session and pool struct should be located in the same package
// --- session
type session struct {
    sessionID uint64
    username  string
}

func (s *session) ID() uint64 {
    return s.sessionID
}

func (s *session) Username() string {
    return s.username
}

// --- pool
type pool struct {
    lock     sync.RWMutex
    sessions map[string]*session
}

func NewSessionPool() *pool {
    p := &pool{
        sessions: make(map[string]*session),
    }

    return p
}

func (p *pool) Set(sid string, s Session) {
    p.lock.Lock()
    defer p.lock.Unlock()

    p.sessions[sid] = &session{s.ID(), s.Username()}
}

func (p *pool) Get(sid string) (Session, bool) {
    p.lock.RLock()
    defer p.lock.RUnlock()

    s, ok := p.sessions[sid]
    return s, ok
}

//  SessionPoolInfo located in different package and depends on SessionPool
// --- SessionPoolInfo
type SessionPoolInfo struct {
    sessionPool SessionPool
}

func NewSessionPoolInfo(sp SessionPool) *SessionPoolInfo {
    return &SessionPoolInfo{
        sessionPool: sp,
    }
}

func (si *SessionPoolInfo) PrintInfo(sid string) {
    if session, ok := si.sessionPool.Get(sid); ok {
        fmt.Printf("Session ID: %d, Username: %s\n", session.ID(), session.Username())
    } else {
        fmt.Printf("session not found (ID: %s)\n", sid)
    }
}

func main() {
    sp := NewSessionPool()
    s := &session{
        sessionID: 1,
        username:  "John Doe",
    }
    sp.Set("1", s)

    spi := NewSessionPoolInfo(sp)
    spi.PrintInfo("1")
}
rbl8hiat

rbl8hiat3#

您可以使用sync.Once类型来确保这一点。sync。Once是一个只执行一个操作的对象。

import (
    "fmt"
    "sync"
)
.
.
.
var p *pool
var once sync.Once
.
.
.
// Pool returns default pool instance
// it initialised the pool if not initilaised
func Pool() *pool {
    once.Do(func() {
        p = &pool{
            sessions: map[string]*session{},
        }
    })
    return p
}

这里是一个link到操场

h79rfbju

h79rfbju4#

首先,你可能不想要一个单例,更有可能的是,你想创建一个结构体,然后传递它沿着或者更好地传递一个表示你想要的行为的接口。也就是说,您可以使用init()函数来初始化单例

type Singleton struct {
    Message string
}

var MyGlobalStruct *Singleton

func init() {
    MyGlobalStruct = &Singleton{Message: "hello world"}
}

func main() {
    fmt.Println(MyGlobalStruct.Message)
}

相关问题