前言 整理自刘丹冰老师的《Easy 搞定 Golang设计模式》
说是整理,到最后发现基本都是文章中的内容:p
Go中的组合与继承 参考文章:go继承
go中没有继承,只能通过组合来实现继承
继承就是子类继承了父类的特征和行为,使得子类实例具有父类的行为和方法 组合就是通过对现有对象的拼装从而获得实现更为复杂的行为的方法
一个struct嵌套了另外一个匿名的struct从而实现了继承,嵌套多个匿名struct实现多重继承。
一个struct嵌套了另外一个struct的实例实现了组合。
示例出处:go结构体的“继承”
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 type Animal struct { name string } func (a *Animal) move() { fmt.Printf("%s会动!\n" , a.name) } type Dog struct { Feet int8 *Animal } func (d *Dog) wang() { fmt.Printf("%s会汪汪汪~\n" , d.name) } func main () { d1 := &Dog{ Feet: 4 , Animal: &Animal{ name: "乐乐" , }, } d1.wang() d1.move() }
go继承是通过嵌套匿名struct实现继承。
go继承在本质上还是组合。
子类要调用父类的实现可以通过调用组合中的父类对象的方法。
多重继承中不允许多个父类出现相同的方法。(编译不通过)
设计模式概述 设计模式是在特定环境下人们解决某类重复出现问题的一套成功或有效的解决方案。
GoF给软件设计模式提供了定义,如下:“软件设计模式(Design Pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结,使用设计模式是为了可重用代码、让代码更容易被他人理解并且保证代码可靠性。” 一句大白话可以总结:“在一定环境下,用固定套路解决问题。”
GoF提出的设计模式有23个,包括:
创建型(Creational)模式:如何创建对象;
结构型(Structural )模式:如何实现类或对象的组合;
行为型(Behavioral)模式:类或对象怎样交互以及怎样分配职责。
有一个“简单工厂模式”不属于GoF 23种设计模式,但大部分的设计模式书籍都会对它进行专门的介绍。 设计模式目前种类: GoF的23种 + “简单工厂模式” = 24种。
学习设计模式的作用:
如何将代码分散在几个不同的类中?
为什么要有“接口”?
何谓针对抽象编程?
何时不应该使用继承?
如果不修改源代码增加新功能?
更好地阅读和理解现有类库与其他系统中的源代码。
设计模式的基础是:多态。
https://www.yuque.com/aceld/lfhu8y/pebesh?inner=qSWMX
面向对象设计原则 对于面向对象软件系统的设计而言,在支持可维护性的同时,提高系统的可复用性是一个至关重要的问题,如何同时提高一个软件系统的可维护性和可复用性是面向对象设计需要解决的核心问题之一 。在面向对象设计中,可维护性的复用是以设计原则为基础的。每一个原则都蕴含一些面向对象设计的思想,可以从不同的角度提升一个软件结构的设计水平。
面向对象设计原则为支持可维护性复用而诞生,这些原则蕴含在很多设计模式中,它们是从许多设计方案中总结出的指导性原则 。面向对象设计原则也是我们用于评价一个设计模式的使用效果的重要指标之一。
原则的目的: 高内聚,低耦合
名称
定义
单一职责原则 (Single Responsibility Principle, SRP) ★★★★☆
类的职责单一,对外只提供一种功能,而引起类变化的原因都应该只有一个。
开闭原则 (Open-Closed Principle, OCP) ★★★★★
类的改动是通过增加代码进行的,而不是修改源代码。(对扩展开放,对修改封闭)
里氏代换原则 (Liskov Substitution Principle, LSP ★★★★★
任何抽象类(interface接口)出现的地方都可以用他的实现类进行替换,实际就是虚拟机制,语言级别实现面向对象功能。
依赖倒转原则 (Dependence Inversion Principle, DIP) ★★★★★
依赖于抽象(接口),不要依赖具体的实现(类),也就是针对接口编程。
接口隔离原则 (Interface Segregation Principle, ISP ★★☆☆☆
不应该强迫用户的程序依赖他们不需要的接口方法。一个接口应该只提供一种对外功能,不应该把所有操作都封装到一个接口中去。
合成复用原则 (Composite Reuse Principle, CRP) ★★★★☆
如果使用继承,会导致父类的任何变换都可能影响到子类的行为。如果使用对象组合,就降低了这种依赖关系。对于继承和组合,优先使用组合。
迪米特法则 (Law of Demeter, LoD ★★★☆☆
一个对象应当对其他对象尽可能少的了解,从而降低各个对象之间的耦合,提高系统的可维护性。例如在一个程序中,各个模块之间相互调用时,通常会提供一个统一的接口来实现。这样其他模块不需要了解另外一个模块的内部实现细节,这样当一个模块内部的实现发生改变时,不会影响其他模块的使用。(黑盒原理)
单一职责原则 类的职责单一,对外只提供一种功能,而引起类变化的原因都应该只有一个。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 package mainimport "fmt" type ClothesShop struct {}func (cs *ClothesShop) OnShop() { fmt.Println("休闲的装扮" ) } type ClothesWork struct {}func (cw *ClothesWork) OnWork() { fmt.Println("工作的装扮" ) } func main () { cw := new (ClothesWork) cw.OnWork() cs := new (ClothesShop) cs.OnShop() }
在面向对象编程的过程中,设计一个类,建议对外提供的功能单一,接口单一,影响一个类的范围就只限定在这一个接口上,一个类的一个接口具备这个类的功能含义,职责单一不复杂。
开闭原则 对扩展开放,对修改封闭
对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。
对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对已有代码进行任何修改
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 package mainimport "fmt" type AbstractBanker interface { DoBusi() } type SaveBanker struct { } func (sb *SaveBanker) DoBusi() { fmt.Println("进行了存款" ) } type TransferBanker struct { } func (tb *TransferBanker) DoBusi() { fmt.Println("进行了转账" ) } type PayBanker struct { } func (pb *PayBanker) DoBusi() { fmt.Println("进行了支付" ) } func BankerBusiness (banker AbstractBanker) { banker.DoBusi() } func main () { BankerBusiness(&SaveBanker{}) BankerBusiness(&TransferBanker{}) BankerBusiness(&PayBanker{}) }
依赖倒转原则 依赖于抽象(接口),不要依赖具体的实现(类),也就是针对接口编程
耦合度极高的模块关系设计
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 package mainimport "fmt" type Benz struct {}func (this *Benz) Run() { fmt.Println("Benz is running..." ) } type BMW struct {}func (this *BMW) Run() { fmt.Println("BMW is running ..." ) } type Zhang3 struct { } func (zhang3 *Zhang3) DriveBenZ(benz *Benz) { fmt.Println("zhang3 Drive Benz" ) benz.Run() } func (zhang3 *Zhang3) DriveBMW(bmw *BMW) { fmt.Println("zhang3 drive BMW" ) bmw.Run() } type Li4 struct { } func (li4 *Li4) DriveBenZ(benz *Benz) { fmt.Println("li4 Drive Benz" ) benz.Run() } func (li4 *Li4) DriveBMW(bmw *BMW) { fmt.Println("li4 drive BMW" ) bmw.Run() } func main () { benz := &Benz{} zhang3 := &Zhang3{} zhang3.DriveBenZ(benz) bmw := &BMW{} li4 := &Li4{} li4.DriveBMW(bmw) }
假设现在要增加一个 丰田汽车 或者 司机王五 , 那么模块和模块的依赖关系将成指数级递增,想蜘蛛网一样越来越难维护和捋顺
面向抽象层依赖倒转 在设计一个系统的时候,将模块分为3个层次,抽象层、实现层、业务逻辑层。那么,我们首先将抽象层的模块和接口定义出来,这里就需要了interface接口的设计,然后我们依照抽象层,依次实现每个实现层的模块,在我们写实现层代码的时候,实际上我们只需要参考对应的抽象层实现就好了,实现每个模块,也和其他的实现的模块没有关系,这样也符合了上面介绍的开闭原则。这样实现起来每个模块只依赖对象的接口,而和其他模块没关系,依赖关系单一。系统容易扩展和维护。
在指定业务逻辑也是一样,只需要参考抽象层的接口来业务就好了,抽象层暴露出来的接口就是我们业务层可以使用的方法,然后可以通过多态的线下,接口指针指向哪个实现模块,调用了就是具体的实现方法,这样我们业务逻辑层也是依赖抽象成编程。
我们就将这种的设计原则叫做 依赖倒转原则
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 package mainimport "fmt" type Car interface { Run() } type Driver interface { Drive(car Car) } type BenZ struct { } func (benz * BenZ) Run() { fmt.Println("Benz is running..." ) } type Bmw struct { } func (bmw * Bmw) Run() { fmt.Println("Bmw is running..." ) } type Zhang_3 struct { } func (zhang3 *Zhang_3) Drive(car Car) { fmt.Println("Zhang3 drive car" ) car.Run() } type Li_4 struct { } func (li4 *Li_4) Drive(car Car) { fmt.Println("li4 drive car" ) car.Run() } func main () { var bmw Car bmw = &Bmw{} var zhang3 Driver zhang3 = &Zhang_3{} zhang3.Drive(bmw) var benz Car benz = &BenZ{} var li4 Driver li4 = &Li_4{} li4.Drive(benz) }
合成复用原则 如果使用继承,会导致父类的任何变换都可能影响到子类的行为。如果使用对象组合,就降低了这种依赖关系。对于继承和组合,优先使用组合。
迪米特法则 一个对象应当对其他对象尽可能少的了解,从而降低各个对象之间的耦合,提高系统的可维护性。
例如在一个程序中,各个模块之间相互调用时,通常会提供一个统一的接口来实现。这样其他模块不需要了解另外一个模块的内部实现细节,这样当一个模块内部的实现发生改变时,不会影响其他模块的使用。(黑盒原理)
创建型模式 简单工厂模式 为什么需要工厂 没有工厂模式,在开发者创建一个类的对象时,如果有很多不同种类的对象将会如何实现,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package mainimport "fmt" type Fruit struct { } func (f *Fruit) Show(name string ) { if name == "apple" { fmt.Println("我是苹果" ) } else if name == "banana" { fmt.Println("我是香蕉" ) } else if name == "pear" { fmt.Println("我是梨" ) } } func NewFruit (name string ) *Fruit { fruit := new (Fruit) if name == "apple" { } else if name == "banana" { } else if name == "pear" { } return fruit } func main () { apple := NewFruit("apple" ) apple.Show("apple" ) banana := NewFruit("banana" ) banana.Show("banana" ) pear := NewFruit("pear" ) pear.Show("pear" ) }
业务逻辑层 —> 基础类模块
在Fruit类中包含很多“if…else…”代码块,整个类的代码相当冗长,代码越长,阅读难度、维护难度和测试难度也越大;而且大量条件语句的存在还将影响系统的性能,程序在执行过程中需要做大量的条件判断
Fruit类的职责过重,它负责初始化和显示所有的水果对象,将各种水果对象的初始化代码和显示代码集中在一个类中实现,违反了“单一职责原则” ,不利于类的重用和维护
当需要增加新类型的水果时,必须修改Fruit类的构造函数NewFruit()和其他相关方法源代码,违反了“开闭原则”
在中间加一层工厂模块层,来降低业务逻辑层对基础模块层的直接依赖和耦合关联。
业务逻辑层 —> 工厂模块 —> 基础类模块
简单工厂模式角色和职责 简单工厂模式并不属于GoF的23种设计模式。他是开发者自发认为的一种非常简易的设计模式,其角色和职责如下:
工厂 :简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类可以被外界直接调用,创建所需的产品对象。
抽象产品 :简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
具体产品 :简单工厂模式所创建的具体实例对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 package mainimport "fmt" type Fruit interface { Show() } type Apple struct { Fruit } func (apple *Apple) Show() { fmt.Println("我是苹果" ) } type Banana struct { Fruit } func (banana *Banana) Show() { fmt.Println("我是香蕉" ) } type Pear struct { Fruit } func (pear *Pear) Show() { fmt.Println("我是梨" ) } type Factory struct {}func (fac *Factory) CreateFruit(kind string ) Fruit { var fruit Fruit if kind == "apple" { fruit = new (Apple) } else if kind == "banana" { fruit = new (Banana) } else if kind == "pear" { fruit = new (Pear) } return fruit } func main () { factory := new (Factory) apple := factory.CreateFruit("apple" ) apple.Show() banana := factory.CreateFruit("banana" ) banana.Show() pear := factory.CreateFruit("pear" ) pear.Show() }
优缺点 优点:
实现了对象创建和使用的分离
不需要记住具体类名,记住参数即可,减少使用者记忆量。
缺点:
对工厂类职责过重,一旦不能工作,系统受到影响
增加系统中类的个数,复杂度和理解度增加
违反“开闭原则”,添加新产品需要修改工厂逻辑,工厂越来越复杂
适用场景
工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂
客户端只知道传入工厂类的参数,对于如何创建对象并不关心
工厂方法模式 角色和职责
抽象工厂 :工厂方法模式的核心,任何工厂类都必须实现这个接口。
工厂 :具体工厂类是抽象工厂的一个实现,负责实例化产品对象。
抽象产品 :工厂方法模式所创建的所有对象的父类,它负责描述所有实例所共有的公共接口。
具体产品 :工厂方法模式所创建的具体实例对象。
简单工厂模式 + “开闭原则” = 工厂方法模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 package mainimport "fmt" type Fruit interface { Show() } type AbstractFactory interface { CreateFruit() Fruit } type Apple struct { Fruit } func (apple *Apple) Show() { fmt.Println("我是苹果" ) } type Banana struct { Fruit } func (banana *Banana) Show() { fmt.Println("我是香蕉" ) } type Pear struct { Fruit } func (pear *Pear) Show() { fmt.Println("我是梨" ) } type JapanApple struct { Fruit } func (jp *JapanApple) Show() { fmt.Println("我是日本苹果" ) } type AppleFactory struct { AbstractFactory } func (fac *AppleFactory) CreateFruit() Fruit { var fruit Fruit fruit = new (Apple) return fruit } type BananaFactory struct { AbstractFactory } func (fac *BananaFactory) CreateFruit() Fruit { var fruit Fruit fruit = new (Banana) return fruit } type PearFactory struct { AbstractFactory } func (fac *PearFactory) CreateFruit() Fruit { var fruit Fruit fruit = new (Pear) return fruit } type JapanAppleFactory struct { AbstractFactory } func (fac *JapanAppleFactory) CreateFruit() Fruit { var fruit Fruit fruit = new (JapanApple) return fruit } func main () { var appleFac AbstractFactory appleFac = new (AppleFactory) var apple Fruit apple = appleFac.CreateFruit() apple.Show() var bananaFac AbstractFactory bananaFac = new (BananaFactory) var banana Fruit banana = bananaFac.CreateFruit() banana.Show() var pearFac AbstractFactory pearFac = new (PearFactory) var pear Fruit pear = pearFac.CreateFruit() pear.Show() var japanAppleFac AbstractFactory japanAppleFac = new (JapanAppleFactory) var japanApple Fruit japanApple = japanAppleFac.CreateFruit() japanApple.Show() }
优缺点 优点:
不需要记住具体类名,甚至连具体参数都不用记忆
实现了对象创建和使用的分离
系统的可扩展性也就变得非常好,无需修改接口和原类
对于新产品的创建,符合开闭原则
缺点:
增加系统中类的个数,复杂度和理解度增加
增加了系统的抽象性和理解难度
适用场景
客户端不知道它所需要的对象的类
抽象工厂类通过其子类来指定创建哪个对象
抽象工厂方法模式 工厂方法模式通过引入工厂等级结构,解决了简单工厂模式中工厂类职责太重的问题,但由于工厂方法模式中的每个工厂只生产一类产品,可能会导致系统中存在大量的工厂类,势必会增加系统的开销。
因此,可以考虑将一些相关的产品组成一个“产品族”,由同一个工厂来统一生产,这就是本文将要学习的抽象工厂模式的基本思想。
“抽象工厂方法模式”引出了“产品族”和“产品等级结构”概念,其目的是为了更加高效的生产同一个产品组产品
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 package mainimport "fmt" type AbstractApple interface { ShowApple() } type AbstractBanana interface { ShowBanana() } type AbstractPear interface { ShowPear() } type AbstractFactory interface { CreateApple() AbstractApple CreateBanana() AbstractBanana CreatePear() AbstractPear } type ChinaApple struct {}func (ca *ChinaApple) ShowApple() { fmt.Println("中国苹果" ) } type ChinaBanana struct {}func (cb *ChinaBanana) ShowBanana() { fmt.Println("中国香蕉" ) } type ChinaPear struct {}func (cp *ChinaPear) ShowPear() { fmt.Println("中国梨" ) } type ChinaFactory struct {}func (cf *ChinaFactory) CreateApple() AbstractApple { var apple AbstractApple apple = new (ChinaApple) return apple } func (cf *ChinaFactory) CreateBanana() AbstractBanana { var banana AbstractBanana banana = new (ChinaBanana) return banana } func (cf *ChinaFactory) CreatePear() AbstractPear { var pear AbstractPear pear = new (ChinaPear) return pear } type JapanApple struct {}func (ja *JapanApple) ShowApple() { fmt.Println("日本苹果" ) } type JapanBanana struct {}func (jb *JapanBanana) ShowBanana() { fmt.Println("日本香蕉" ) } type JapanPear struct {}func (cp *JapanPear) ShowPear() { fmt.Println("日本梨" ) } type JapanFactory struct {}func (jf *JapanFactory) CreateApple() AbstractApple { var apple AbstractApple apple = new (JapanApple) return apple } func (jf *JapanFactory) CreateBanana() AbstractBanana { var banana AbstractBanana banana = new (JapanBanana) return banana } func (cf *JapanFactory) CreatePear() AbstractPear { var pear AbstractPear pear = new (JapanPear) return pear } type AmericanApple struct {}func (aa *AmericanApple) ShowApple() { fmt.Println("美国苹果" ) } type AmericanBanana struct {}func (ab *AmericanBanana) ShowBanana() { fmt.Println("美国香蕉" ) } type AmericanPear struct {}func (ap *AmericanPear) ShowPear() { fmt.Println("美国梨" ) } type AmericanFactory struct {}func (af *AmericanFactory) CreateApple() AbstractApple { var apple AbstractApple apple = new (AmericanApple) return apple } func (af *AmericanFactory) CreateBanana() AbstractBanana { var banana AbstractBanana banana = new (AmericanBanana) return banana } func (af *AmericanFactory) CreatePear() AbstractPear { var pear AbstractPear pear = new (AmericanPear) return pear } func main () { var aFac AbstractFactory aFac = new (AmericanFactory) var aApple AbstractApple aApple = aFac.CreateApple() aApple.ShowApple() var aBanana AbstractBanana aBanana = aFac.CreateBanana() aBanana.ShowBanana() var aPear AbstractPear aPear = aFac.CreatePear() aPear.ShowPear() cFac := new (ChinaFactory) cApple := cFac.CreateApple() cApple.ShowApple() cBanana := cFac.CreateBanana() cBanana.ShowBanana() }
优缺点 优点:
拥有工厂方法模式的优点
当一个产品族中的多个对象被设计成一起工作时,它能够保证客户端始终只使用同一个产品族中的对象
增加新的产品族很方便,无须修改已有系统,符合“开闭原则”
缺点:
增加新的产品等级结构麻烦,需要对原有系统进行较大的修改,甚至需要修改抽象层代码,这显然会带来较大的不便,违背了“开闭原则”
使用场景
系统中有多于一个的产品族。而每次只使用其中某一产品族。可以通过配置文件等方式来使得用户可以动态改变产品族,也可以很方便地增加新的产品族
产品等级结构稳定。设计完成之后,不会向系统中增加新的产品等级结构或者删除已有的产品等级结构
类似的实现有:
设计一个电脑主板架构,电脑包括(显卡,内存,CPU)3个固定的插口,显卡具有显示功能(display,功能实现只要打印出意义即可),内存具有存储功能(storage),cpu具有计算功能(calculate)。 现有Intel厂商,nvidia厂商,Kingston厂商,均会生产以上三种硬件。 要求组装两台电脑, 1台(Intel的CPU,Intel的显卡,Intel的内存) 1台(Intel的CPU, nvidia的显卡,Kingston的内存) 用抽象工厂模式实现。
单例模式 保证一个类永远只能有一个对象,且该对象的功能依然能被其他模块使用。
分为恶汉式、懒汉式
恶汉式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 package mainimport "fmt" type singelton struct {}var instance *singelton = new (singelton)func GetInstance () *singelton { return instance } func (s *singelton) SomeThing() { fmt.Println("单例对象的某方法" ) } func main () { s := GetInstance() s.SomeThing() }
sync.once
实现懒汉式的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package mainimport ( "fmt" "sync" ) var once sync.Oncetype singelton struct {}var instance *singeltonfunc GetInstance () *singelton { once.Do(func () { instance = new (singelton) }) return instance } func (s *singelton) SomeThing() { fmt.Println("单例对象的某方法" ) } func main () { s := GetInstance() s.SomeThing() }
优缺点 优点:
单例模式提供了对唯一实例的受控访问
节约系统资源(在系统内存中只存在一个对象)
缺点:
扩展略难(单例模式中没有抽象层)
单例类的职责过重
适用场景
系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器或资源管理器,或者需要考虑资源消耗太大而只允许创建一个对象
客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例