1. 单例模式
单例模式的定义是:保证一个类仅有一个实例,并提供一个访问它的全局访问点。简单的说就是希望我们的系统中该类仅仅存在1个或0个该类的实例。
1.1 非并发情况
提供了一个GetInstance
函数去获取它的实例,根据是否为空来判断究竟应该创建新的还是沿用旧的。用系统时间作为鉴别依据。编写时主要考虑:
- 实例具有全局性
- 创建前先判断
type manager struct {
t time.Time
}
func (p manager) manage(){
s:=fmt.Sprintf("time=%s, managing...",p.t)
fmt.Println(s)
}
var m *manager
func GetInstance() *manager{
if m==nil{
m=new(manager)
m.t=time.Now()
}
return m
}
func main(){
a:=GetInstance()
a.manage()
b:=GetInstance()
b.manage()//二者输出相同时间,证明单例成功
}
1.2 线程安全
老问题:if m==nil
语句到m=new(manager)
之间并不安全,可能创建多次。
方法一,可以用锁锁一下:
var m *manager
var mutex sync.Mutex //注意要import sync
func GetInstance() *manager{
mutex.Lock()
defer mutex.Unlock()
if m==nil{
m=new(manager)
m.t=time.Now()
}
return m
}
方法二,提高效率可以采用双重锁机制,前者每次都会锁,双重锁机制下只有部分情况才会锁。但双重锁有个问题:new是先创建指针然后分配内存,如果在这中间被if抢先了就会造成误操作,多生成一个manager
var m *Manager
var lock *sync.Mutex = &sync.Mutex {}
func GetInstance() *Manager {
if m == nil {
lock.Lock()
defer lock.Unlock()
if m == nil {
m = new(manager)
}
}
return m
}
方法三,sync.Once
,它有一个Do方法,在它中的函数go会只保证仅仅调用一次。跟C++的`std::call_once
函数是一个意思。
var m *manager
var once sync.Once
func GetInstance() *manager{
lambda:=func(){
m=new(manager)
}
once.Do(lambda)
return m
}
2. 工厂模式
2.1 简单工厂模式
定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。
下面看个例子:
项目 | 说明 |
---|---|
Phone | 接口,只有一个方法来获取电池容量 |
type Huawei struct | 华为手机构造体 |
type Xiaomi struct | 小米手机构造体 |
func (p* Huawei) get Battery | 华为手机的GetBattery实现 |
func (p* Xiaomi) get Battery | 小米手机的GetBattery实现 |
type PhoneFactory struct | 手机工厂的构造体 |
func (factory PhoneFactory) CreatePhone(brand string) Phone | 手机工厂的CreatePhone实现 |
注意GetBattery函数不是必须的,这里只是方便演示。要注意工厂生产函数的返回类型,phone的接口类型。
//phone作为接口,统一方法
type Phone interface{
getBattery()
}
//华为
type Huawei struct{}
func (p* Huawei) getBattery(){
fmt.Println("[Huawei battery]:4500mAn")
}
//小米
type Xiaomi struct{}
func (p* Xiaomi) getBattery(){
fmt.Println("[Xiaomi battery]:4400mAn")
}
//工厂生产
type PhoneFactory struct {
}
func (p* PhoneFactory) Creat(brand string) Phone{
switch brand {
case "Huawei":
return new(Huawei)
case "Xiaomi":
return new(Xiaomi)
default:
return nil
}
}
func main(){
var phone Phone
factory:=new(PhoneFactory)
phone=factory.Creat("Huawei")
phone.getBattery()
phone=factory.Creat("Xiaomi")
phone.getBattery()
}
2.2 工厂方法模式
- 简单工厂需要:
- 工厂结构体
- 产品接口
- 产品结构体
假设餐馆生产面条和米饭,如果只有一个餐馆则简单工厂模式即可解决,但如果有开了分店,有多个工厂,就需要工厂方法模式了。
- 工厂方法需要:
- 工厂接口
- 工厂结构体
- 产品接口
- 产品结构体
//Step1. 添加接口
//工厂接口
type FactoryInterface interface {
Generate(s string) ProductInterface //继承自产品接口
}
// 产品接口
type ProductInterface interface {
create()
}
//Step2. 创建产品结构体及方法
type RiceShopA struct {}
type NoodleShopA struct {}
type RiceShopB struct {}
type NoodleShopB struct {}
func (p* RiceShopA) create() {
fmt.Println("A店米饭")
}
func (p* NoodleShopA) create() {
fmt.Println("A店面条")
}
func (p* RiceShopB) create() {
fmt.Println("B店米饭")
}
func (p* NoodleShopB) create() {
fmt.Println("B店面条")
}
//Step3. 创建工厂及方法
type shopA struct{}
type shopB struct{}
func (p* shopA) Generate(s string)ProductInterface{
switch s{
case "rice":
return new(RiceShopA)
case "noodle":
return new(NoodleShopA)
default:
return nil
}
}
func (p* shopB) Generate(s string)ProductInterface{
switch s{
case "rice":
return new(RiceShopB)
case "noodle":
return new(NoodleShopB)
default:
return nil
}
}
func main(){
newshopA:=new(shopA)
a:=newshopA.Generate("rice")
a.create()
newshopB:=new(shopB)
b:=newshopB.Generate("noodle")
b.create()
}
2.3 抽象工厂模式
与工厂方法模式的区别在于:工厂方法模式是一个工厂接口创建了一个方法,一个方法对应了两个工厂结构体,而抽象工厂模式是一个工厂接口创建了两个方法生产。
假设有广东包子铺和祁山包子铺两家,都生产猪肉和三鲜包子:
type FactoryInterface interface {
CreatePigMeatBuns() ProductInterface // 创建猪肉馅产品
Create3SBuns() ProductInterface // 创建三鲜馅产品
}
type ProductInterface interface {
Intro()
}
然后实现4种产品:
type GDPigMeatBuns struct {
}
func (p GDPigMeatBuns) Intro() {
fmt.Println("广东猪肉馅包子")
}
// TODO ... 其他产品实现方法没区别
之后实现工厂:
// 齐市包子铺
type QSFactory struct {
}
func (qs QSFactory) CreatePigMeatBuns() ProductInterface {
return QSPigMeatBuns{}
}
func (qs QSFactory) Create3SBuns() ProductInterface {
return QS3SBuns{}
}
// 广东包子铺
type GDFactory struct {
}
func (gd GDFactory) CreatePigMeatBuns() ProductInterface {
return GDPigMeatBuns{}
}
func (gd GDFactory) Create3SBuns() ProductInterface {
return GD3SBuns{}
}
最后实际生产:
var f FactoryInterface // 特意以这种方式声明,更好的体会抽象工厂模式的好处
f = new(QSFactory)
b := f.CreatePigMeatBuns()
b.Intro()
优点: 抽象工厂模式除了具有工厂方法模式的优点外,最主要的优点就是可以在类的内部对产品族进行约束。所谓的产品族,一般或多或少的都存在一定的关联,抽象工厂模式就可以在类内部对产品族的关联关系进行定义和描述,而不必专门引入一个新的类来进行管理。
缺点: 产品族的扩展将是一件十分费力的事情,假如产品族中需要增加一个新的产品,则几乎所有的工厂类都需要进行修改。所以使用抽象工厂模式时,对产品等级结构的划分是非常重要的
3. 策略模式
体现的原则是对扩展开放,对修改关闭(开闭原则) ,如果我们要增加一些规则,完全不用修改主业务流程,只需要增加几个策略即可。
假设创建加减乘除方法:
//策略接口
type Strategier interface {
Compute(num1, num2 int) int
}
//策略之一:除法
type division struct{}
func (p* division) Compute(num1,num2 int) int{
if num2 == 0 {
panic("num2 must not be 0!")
}
return num1 / num2
}
func NewStrategy(t string)(res Strategier){
switch t {
case "d":
res=new(division)
default:
res=nil
}
return
}
func main(){
Strategy:=NewStrategy("d")
val:=Strategy.Compute(20,10)
fmt.Println(val)
}
4. 观察者模式
多个对象同时观察一个对象,当这个被观察的对象发生变化的时候,这些对象都会得到通知,可以做一些操作。
核心要点:
- 观察者需要接收观察变化的函数
- 被观察者要能创建观察者
- 被观察者要能通知观察者
//创建观察者
type IObserver interface{
Notify()
}
//创建被观察者
type IObject interface{
AddObserver(observers Observer)
NotifyObservers()
}
type Observer struct{}
func (p* Observer) Notify(){
fmt.Println("已触发观察者")
}
type Obeject struct{
Observers []IObserver
}
func (p* Obeject) AddObserver(observer []IObserver){
p.Observers=append(p.Observers, observer...)
}
func (p* Obeject) NotifyObserver(){
for k:=range p.Observers{
p.Observers[k].Notify()
}
}
func main(){
// 创建被观察者
s := new(Obeject)
// 创建观察者
o := make([]IObserver,0)
o=append(o, new(Observer))
o=append(o, new(Observer))
// 为主题添加观察者
s.AddObserver(o)
s.NotifyObserver()
}
5. 代理模式
举两个生活化的例子:
- 火车票的代理售票点。代售点就是代理,它拥有被代理对象的部分功能 — 售票功能
- 明星的经纪人,经纪人就是代理,负责为明星处理一些事务。
type seller interface{
sell(consumer string)
}
type Station struct{ //真正的销售者
stock int //存货
}
func (p* Station)sell(consumer string){
if p.stock>0{
p.stock--
fmt.Printf("火车站,客户:%s买了一张票,剩余:%d \n", consumer,p.stock)
}else{
fmt.Println("已售空")
}
}
type Xiecheng struct{ //携程代理
station *Station //持有一个火车站
}
func (p* Xiecheng)sell(consumer string){
if p.station.stock>0{
p.station.stock--
fmt.Printf("携程网,客户:%s买了一张票,剩余:%d \n", consumer,p.station.stock)
}else{
fmt.Println("已售空")
}
}
func main(){
sta:=Station{3}
proxy:=new(Xiecheng)
proxy.station=&sta
proxy.sell("Bob")
sta.sell("Lily")
}
6. 生产者消费者模式
6.1 无缓冲
**
var wg sync.WaitGroup
func producer(ch chan<-int){
defer wg.Done()
for i:=0;i<3;i++{
ch<-i
fmt.Println("send:",i)
}
}
func consumer(ch <-chan int){
defer wg.Done()
for i:=0;i<3;i++{
v:=<-ch
fmt.Println("received:",v)
}
}
func main(){
wg.Add(2)
ch:=make(chan int)
go producer(ch)
go consumer(ch)
wg.Wait()
}
因为channel没有缓冲区,所以当生产者给channel赋值后会进入阻塞状态。消费者取出一次数据后由于没有数据可读,也会进入阻塞。输出如下:
send: 0
received: 0
send: 1
received: 1
send: 2
received: 2
如果没有按这个顺序输出,是print输出缓冲区的问题,加个50ms延时即可。
6.2 有缓冲
只需要在ch创建时改为
ch := make(chan int, 10)
输出:
send: 0
send: 1
send: 2
received: 0
received: 1
received: 2