您现在的位置是:首页 >学无止境 >Go常用设计模式网站首页学无止境

Go常用设计模式

西直门三太子 2023-06-30 16:00:03
简介Go常用设计模式

单例模式

饿汉模式

饿的要死就先吃,上来就给你初始化

package instance

type instance struct{}

// 饿汉,此包引入就初始化完成,不管你用不用,反正我是放到内存里去了
var i instance = instance{}

func getInstance() instance {
	return i
}
懒汉模式

等我感觉要死了再吃,就是我确定要用到,才会去实例化

package instance

import "sync"

type instance struct{}

var i *instance
var mu sync.Locker

// 确定使用时,才初始化,需要加锁保证并发安全

func getInstance() *instance {
	mu.Lock()
	if i == nil {
		i = &instance{}
	}
	mu.Unlock()
	return i
}

优雅进阶版

package instance

import "sync"

type instance struct{}

var i *instance
var once sync.Once

func getInstance() *instance {
	// 确实看起来舒服点,使用 once 保证只初始化一次
	once.Do(func() {
		i = &instance{}
	})
	return i
}

工厂模式

简单工厂

返回一个具体的实例

package main

type bird struct {
	name string
}

// NewBigBird 工厂模式的好处在于不会让你忘记传参数
func NewBigBird(name string) bird {
	return bird{
		name: name,
	}
}

// BAD 这啥鸟?
var b = bird{}

// GOOD
var c = NewBigBird("百灵鸟")

抽象工厂

和简单工厂的区别在于其返回的是接口类型

package main

import "fmt"

type Bird interface {
	fly()
}

type bird struct {
	name string
}

func (b bird) fly() {
	fmt.Printf("%s 在飞", b.name)
}

// NewBigBird 返回的是接口类型
func NewBigBird(name string) Bird {
	return bird{
		name: name,
	}
}

工厂方法

工厂方法 提供创建实例的方法,由外部决定是否创建,或者创建的(规格),适用于多规格的产品,比较灵活

package main

import "fmt"

type Bird interface {
	fly()
}

type bird struct {
	name  string
	color string
}

func (b bird) fly() {
	fmt.Printf("%s %s 在飞 
", b.color, b.name)
}

func NewBigBird(name string) func(color string) bird {
	return func(color string) bird {
		return bird{
			name:  name,
			color: color,
		}
	}
}

func newReadBird(name string) bird {
	return bird{
		name:  name,
		color: "红色",
	}
}

func newBlueBird(name string) bird {
	return bird{
		name:  name,
		color: "蓝色",
	}
}

func newBird(name, color string) bird {
	return bird{
		name:  name,
		color: color,
	}
}

func main() {
	//1 工厂方法 提供创建实例的方法,由外部决定是否创建,或者创建的(规格),适用于多规格的产品,比较灵活
	register := NewBigBird("喜鹊")
	read := register("红色")
	blue := register("蓝色")
	read.fly()
	blue.fly()

	//2 简单工厂 在来个绿喜鹊 还需要创建 newGreen
	read2 := newReadBird("喜鹊")
	blue2 := newBlueBird("喜鹊")
	read2.fly()
	blue2.fly()

	//3 比2稍微方便点,但是喜鹊是重复的参数
	read3 := newBird("喜鹊", "红色")
	blue3 := newBird("喜鹊", "蓝色")
	read3.fly()
	blue3.fly()
}

策略模式

主要是对算法(策略)的封装,根据不同场景使用不同的策略

package main

import "fmt"

type Strategy interface {
	Do(money float64) float64
}

type normal struct {
}

type strategy51 struct {
}

// Do 劳动节5折
func (s strategy51) Do(money float64) float64 {
	return money * 0.5
}

func (n normal) Do(money float64) float64 {
	return money
}

type Goods struct {
	strategy Strategy
}

func (g *Goods) setStrategy(s Strategy) {
	g.strategy = s
}

func (g *Goods) getPrice(money float64) float64 {
	return g.strategy.Do(money)
}

func main() {
	g := Goods{}
	//平常购买
	g.setStrategy(normal{})
	fmt.Println(g.getPrice(100))

	//劳动节购
	g.setStrategy(strategy51{})
	fmt.Println(g.getPrice(100))
}

模板模式

将共同点抽离生成模板类,可变点强制子类去实现

package main

import "fmt"

type IStudent interface {
	getSchool() string
	getName() string
}

type SchoolTemplate struct {
}

// 同一个学校的
func (s SchoolTemplate) getSchool() string {
	return "黄埔军校"
}

type Student struct {
	Name string
	SchoolTemplate
}

// 强制实现
func (s Student) getName() string {
	return s.Name
}

func introduce(i IStudent) {
	fmt.Printf("我叫%s,来自%s 
", i.getName(), i.getSchool())
}

/*
*
由于小明和小白是一个学校的,抽离出学校模板类 SchoolTemplate
姓名不同,交给 Student 去实现
*/
func main() {
	s := Student{Name: "小明"}
	b := Student{Name: "小白"}
	introduce(s)
	introduce(b)
}

代理模式

代理模式对被代理的对象进行访问控制,把主机看做一个类,那么路由器就是代理者,代理者可以决定是否对其开放网络,和获取主机网络的具体信息

package main

import "fmt"

type Computer interface {
	Connect()
}

type Proxy struct {
	computer Computer
}

type Network struct {
}

func (n Network) Connect() {
	fmt.Println("连接网络")
}

func (p Proxy) Connect(Ip string) {
	// 对被代理的网络连接进行访问控制
	if Ip == "6.6.6.6" {
		fmt.Println("黑名单,禁止连接")
		return
	}
	p.computer.Connect()
}

func main() {
	proxy := Proxy{computer: Network{}}
	proxy.Connect("6.6.6.6")
	proxy.Connect("5.5.5.5")
}

选项模式

不管什么方式,都应该提供一个最佳配置的生成
选项模式是指 example3

example1
  1. 生成的方法有点多(newDefaultExample,newExample)
package main

import "fmt"

var (
	aDefault = "a"
	bDefault = "b"
	cDefault = "c"
	dDefault = "d"
	eDefault = "e"
)

type example struct {
	a string
	b string
	c string
	d string
	e string
}

func newDefaultExample() example {
	return example{
		a: aDefault,
		b: bDefault,
		c: cDefault,
		d: dDefault,
		e: eDefault,
	}
}

func newExample(a, b, c, d, e string) example {
	return example{
		a: a,
		b: b,
		c: c,
		d: d,
		e: e,
	}
}

func main() {
	fmt.Println(newDefaultExample())
	fmt.Println(newExample("1", "2", "3", "4", "5"))
}

example2
  1. 在example1的基础上,减少了生成方式(newDefaultExample),newExample 由 外部传入的参数对象决定生成的配置
  2. 但是参数对象生成的方式还是有点多(newDefaultOpt,options{})
  3. (options) 选项填的还是有点多,希望没填选项就使用最佳默认值(请看example3)
package main

import "fmt"

var (
	aDefault = "a"
	bDefault = "b"
	cDefault = "c"
	dDefault = "d"
	eDefault = "e"
)

type example struct {
	a string
	b string
	c string
	d string
	e string
}

type options struct {
	a string
	b string
	c string
	d string
	e string
}

func newDefaultOpt() options {
	return options{
		a: "a1",
		b: "b1",
		c: "c1",
		d: "d1",
		e: "e1",
	}
}

func newExample(opt options) example {
	return example{
		a: opt.a,
		b: opt.b,
		c: opt.c,
		d: opt.d,
		e: opt.e,
	}
}

/*
*
在example1的基础上,减少了创建函数,外部传入的参数决定生成struct
但是创建选项的函数有多个
*/
func main() {
	fmt.Println(newExample(newDefaultOpt()))
    // 选项填的还是有点多,希望没填选项就使用最佳默认值(请看example3)
	fmt.Println(newExample(options{
		a: "a2",
		b: "b2",
		c: "c2",
		d: "d2",
		e: "e2",
	}))
}

example3
  1. 提供了统一的入口,生成方式就一个
  2. 提供了最佳默认配置
  3. 外部设置了就覆盖默认配置选项,不设置就使用最佳默认配置中的选项信息
  4. 这个比 example1,example2 更灵活,也更复杂,参数不多不建议,但参数多的话,强烈建议
package main

import "fmt"

var (
	aDefault = "a"
	bDefault = "b"
	cDefault = "c"
	dDefault = "d"
	eDefault = "e"
)

type example struct {
	a string
	b string
	c string
	d string
	e string
}

type options struct {
	a string
	b string
	c string
	d string
	e string
}

// IOptions 提供往配置上设置参数值的方法
type IOptions interface {
	apply(opt *options)
}

type optionsFunc func(option *options)

func (f optionsFunc) apply(opt *options) {
	f(opt)
}

func withA(a string) IOptions {
	return optionsFunc(func(option *options) {
		option.a = a
	})
}

func withB(b string) IOptions {
	return optionsFunc(func(option *options) {
		option.b = b
	})
}

// 提供了统一的入口,生成方式就一个
// opt 提供了往配置上设置参数值的方法
// 这个比 example1,example2 更灵活,也更复杂,参数不多不建议,但参数多的话,强烈建议
func newExample(opt ...IOptions) example {

	// 提供了最佳默认配置
	config := &options{
		a: "a",
		b: "b",
		c: "c",
		d: "d",
		e: "e",
	}

	// 外部设置了就覆盖默认配置选项,不设置就使用最佳默认配置选项中的信息
	for _, o := range opt {
		o.apply(config)
	}

	return example{
		a: config.a,
		b: config.b,
		c: config.c,
		d: config.d,
		e: config.e,
	}
}

func main() {
	fmt.Println(newExample())
	fmt.Println(newExample(withA("a1"), withB("b2")))
}

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。