创建型模式(Creational Patterns) 提供了一种在创建对象的同时隐藏创建逻辑的方式,而不是使用 new 运算符直接实例化对象
单例模式(Singleton Pattern)
常用场景:全局共享一个实例,且只需要被初始化一次的场景。例如数据库实例、全局配置、全局任务池等。
单例模式有利于减少内存开支、减少系统性能开销、防止多个实例产生冲突等优点。
饿汉模式
饿汉方式指全局的单例实例在包被加载时创建。
因为实例是在包被导入时初始化的,所以如果初始化耗时,会导致程序加载时间比较长。
package singleton
type singleton struct {
}
var ins *singleton= &singleton{}// 懒汉模式func GetIns() *singleton { return ins}懒汉模式
懒汉方式指全局的单例实例在第一次被使用时创建。
非线程安全版
缺点:非线程安全。当正在创建时,有线程来访问此时ins = nil就会再创建,单例类就会有多个实例了。
package singleton
type singletona struct {}
var insa *singletona// 懒汉模式-非线程安全func GetInsa() *singletona { if insa == nil { insa = &singletona{} } return insa}加锁
虽然解决并发的问题,但每次加锁是要付出代价的,同时使用 defer 将导致锁变成函数级别的锁,其实只锁变量及其初始化就可以了。
package singletonimport "sync"
type singletond struct {
}
var insd *singletondvar mud sync.Mutex// 懒汉模式加锁func Getinsd() *singletond { mud.Lock() defer mud.Unlock() if insd == nil { insd = &singletond{} } return insd}双重检测
通过双重检测,避免了每次加锁,不用defer,避免了函数级别的锁, 从而提高代码效率。
package singleton
import "sync"
type singletonb struct {
}
var insb *singletonbvar mu sync.Mutex// 双重检测实现懒加载func Getinsb() *singletonb { if insb == nil { mu.Lock() if insb == nil{ insb = &singletonb{} } mu.Unlock() } return insb}sync.Once实现
Golang 中特有方式,使用 sync.Once 以确保 ins 实例全局只被创建一次,once.Do 函数还可以确保当同时有多个创建动作时,只有一个创建动作在被执行
package singleton
import "sync"
type singletonc struct {
}
var insc *singletoncvar once sync.Once// 使用once.Do可以确保 ins 实例全局只被创建一次,once.Do 函数还可以确保当同时有多个创建动作时,只有一个创建动作在被执行。func GetInsc() *singletonc{ once.Do(func() { insc = &singletonc{} }) return insc}工厂模式
常用场景:
- 简单工厂模式可以传入参数并返回一个结构体的实例。
- 抽象工厂模式可以返回一个接口,通过返回接口,在不公开内部实现的情况下,让调用者使用你提供的各种功能。
- 工厂方法模式将对象创建从由一个对象负载所有具体类的实例化,变成由一群子类来负责对具体类的实例化,从而将过程解耦。
简单工厂模式
- 在简单工厂模式中,依赖于唯一的工厂对象,如果我们需要实例化一个产品,就要向工厂中传入一个参数,获取对应的对象;
- 如果要增加一种产品,就要在工厂中修改创建产品的函数。这会导致耦合性过高。
package main
import "fmt"
type Drink struct { name string size string}
func (d Drink) DrinkInfo() { fmt.Println("This drink is a %s - %s",d.size,d.name)}// 简单工厂// 确保创建的实例具有需要的参数。func NewDrink(name string, size string) *Drink { return &Drink{ name: name, size: size, }}抽象工厂模式
- 抽象工厂,返回类型是接口,而不是具体结构体
- 通过返回接口,可以实现多个工厂函数,来返回不同的接口实现
- 简单工厂和抽象工厂返回实例对象时,都可以返回指针,也可以返回非指针的实例。
- 实际开发中建议返回非指针的实例。因为一般是想通过创建实例调用其提供的方法,而不是对实例做修改。
- 若想修改,可通过设置Setter方法实现.
- 通过返回非指针实例,可确保实例的属性,避免属性被意外/任意修改。
package main
import "fmt"
type Drink2 interface { DrinkInfo2()}
// 果汁结构体type juice struct { Name string Size string}
func (j juice) DrinkInfo2() { fmt.Println("This drink is a %s - %s",j.Size,j.Name)}
func NewJuice(name string,size string) Drink2 { return juice{ Name: name, Size: size, }}工厂方法模式
针对简单工厂的实现导致耦合性过高的问题,我们就可以使用工厂方法模式。
- 在工厂方法模式中,依赖工厂函数,
- 可以通过实现工厂函数创建多种工厂,
- 将对象创建由一个对象负责所有具体类的实例化,变成由一群子类来负责对具体对象的实例化,从而将过程解耦。
package main
import "fmt"
type Drink3 struct { name string size string}
func (d Drink3) DrinkInfo3() { fmt.Printf("This drink is a %s - %s\n",d.size,d.name)}
func NewDrinkFactory(size string) func(name string) Drink3 { return func(name string) Drink3 { return Drink3{ name: name, size: size, } }}
func main() { // 大杯 newLarge := NewDrinkFactory("large") largejuice :=newLarge("apple") // 中杯 newMedium := NewDrinkFactory("medoim") mediumJuice := newMedium("apple") largejuice.DrinkInfo3() mediumJuice.DrinkInfo3()}
评论