简述网站的建设流程图,网站开发教程全集,交互网站建设需要做什么,公司企业免费网站系统文章目录前言一、函数选项模式二、单例模式三、工厂模式四、责任链模式前言
宿舍每人 温度38℃#xff0b; 大寄 设计模式很重要#xff0c;设计模式其实就是为了解决某一类问题而形成的代码写法#xff0c;设计模式很多#xff0c;但是并不是每个都很常用#xff0c;我们…
文章目录前言一、函数选项模式二、单例模式三、工厂模式四、责任链模式前言
宿舍每人 温度38℃ 大寄 设计模式很重要设计模式其实就是为了解决某一类问题而形成的代码写法设计模式很多但是并不是每个都很常用我们只讲解─些常用的
设计模式分类大家可以参考: https://juejin.cn/post/6908528350986240014
go中最常用的设计模式是函数选项模式, grpckratos等等开源项目中比比皆是
有时候一个函数会有很多参数为了方便函数的使用我们会给希望给一些参数设定默认值调用时只需要传与默认值不同的参数即可类似于python里面的默认参数和字典参数在java中可以提供多种构造函数虽然 golang里面既没有默认参数也没有字典参数但是我们有选项模式
注:函数选项模式是用来构造对象
go中没有构造器要构造一个对象一般是直接实例化或者采用NewXX的模式其中
实例化然后复制属性的模式会写很多行代码虽然可以直接采用A{name:xxx, b:xxx}的模式但 是如果有个c属性的默认值不是空字符串咋办?所以整个过程使用实例化加属性设置的方式会让实例化很麻烦使用new的方式也会有问题:如果可以一个参数设置如果可以两个参数设置不得不使用 newA,newAandB等等各种组合设置有没有办法可以同时解决上面的问题呢?-函数选项模式
选项模式的应用
从这里可以看到为了实现选项的功能我们增加了很多的代码实现成本相对还是较高的所以实践中需要根据自己的业务场景去权衡是否需要使用。个人总结满足下面条件可以考虑使用选项模式
参数确实比较复杂影响调用方使用参数确实有比较清晰明确的默认值为参数的后续拓展考虑
在golang 的很多开源项目里面也用到了选项模式比如 grpc中的 rpc方法就是采用选项模式设计的除了必填的rpc参数外还可以一些选项参数grpc_retry就是通过这个机制实现的可以实现自动重试功能。 一、函数选项模式
函数选项模式Functional Options Pattern 函数选项模式是一种用于函数设计的模式它允许函数接受可选参数这些参数可以通过一种简单、灵活的方式进行设置从而避免出现过多的函数重载。在Go语言中函数选项模式通常使用可变参数列表结合函数类型参数的方式实现。通过该模式我们可以更加灵活地控制函数的行为并且可以方便地扩展函数的功能。 在这个示例代码中DbOptions结构体类型定义了用于保存数据库连接选项的字段。为了实现函数选项模式我们定义了一个名为Option的函数类型用于设置DbOptions结构体中的字段值。WithHost函数是一个实现了Option函数类型的具体函数用于设置数据库连接的主机地址。NewOpts函数接受任意数量的Option函数类型的参数并将其应用于一个DbOptions结构体类型的实例上。
在NewOpts函数中我们首先定义一个带有默认值的DbOptions结构体类型的实例并将其保存在dbopts变量中。接着我们遍历所有传入的Option函数类型参数逐个将其应用于dbopts变量中的字段。最后我们返回dbopts变量的值这是一个包含了所有设置过的选项的DbOptions结构体类型实例。
在main函数中我们调用NewOpts函数获取默认选项并将其打印输出。此时输出的结果中将包含我们在WithHost函数中设置的主机地址选项。这种方式可以使得我们在使用该函数时只需要传递需要设置的选项而不需要关心默认选项或者选项的顺序。
package mainimport fmttype DbOptions struct {Host stringPort intUsername stringPassword stringDBName string
}type Option func(*DbOptions)// 这个函数主要用来设置Host
func WithHost(host string) Option {return func(o *DbOptions) {o.Host host}
}func NewOpts(opts ...Option) DbOptions {//先实例化号dbOptions,填充上默认值dbopts : DbOptions{Host: 127.0.0.1,Port: 3306,Username: root,Password: 123456,DBName: test,}for _, option : range opts {option(dbopts)}return *dbopts
}func main() {//NewDBClient(WithHost(192.168.0.1))//opts : NewOpts(WithHost(192.168.0.1))opts : NewOpts()fmt.Println(opts)//函数选项牧师大量引用了函数
}二、单例模式
单例模式Singleton Pattern 单例模式是一种创建型设计模式它确保一个类只有一个实例并提供了一个全局访问点来访问该实例。在Go语言中单例模式通常使用包级别变量或者全局变量来实现因为包级别变量只会被初始化一次而全局变量则是唯一的。 1. 使用sync.Once实现单例模式 在下面的代码中使用了sync.Once类型来确保在程序运行时只执行一次初始化逻辑以创建唯一的DBPool实例。具体实现步骤如下
a. 在GetDBPool2函数中调用sync.Once的Do方法并将初始化逻辑封装在一个匿名函数中。
b. 在匿名函数中创建DBPool实例并将其赋值给dbPoolIns变量。
c. 返回dbPoolIns变量。
使用sync.Once实现的单例模式具有并发安全性并且无需加锁即可实现懒加载但需要创建匿名函数代码稍微有些复杂。
2. 使用sync.Mutex和atomic实现单例模式 在下面的代码中使用了sync.Mutex和atomic两个包来实现单例模式。具体实现步骤如下
a. 在GetDBPool函数中首先判断initialized变量是否为1如果是则直接返回dbPoolIns变量。
b. 如果initialized变量不是1则获取锁防止其他goroutine同时执行初始化逻辑。
c. 在获取锁后再次判断initialized变量是否为0如果是则创建DBPool实例并将其赋值给dbPoolIns变量然后使用atomic.StoreUint32函数将initialized变量设置为1。
d. 释放锁并返回dbPoolIns变量。
使用sync.Mutex和atomic实现的单例模式也具有并发安全性并且代码比较简单易懂但需要显式加锁并且无法实现懒加载。
package mainimport (syncsync/atomic
)type DBPool struct {Host stringPort intUserName string
}var dbPoolIns *DBPool
var lock sync.Mutex
var initialized uint32// 有问题的方法并发
// 加锁 - 功能上没有问题但是性能不好
// 高并发下有bug
// goroutine1 进来,实例化dbPoolIns DBPool{}进想到一半goroutine2进来读到dbPoolIns ! nil返回dbPoolIns
func GetDBPool() *DBPool {if atomic.LoadUint32(initialized) 1 {return dbPoolIns}lock.Lock()defer lock.Unlock()if initialized 0 {dbPoolIns DBPool{}//原子操作 一旦有一个线程执行到了atomic.LoadUint32(initialized) initialized就会被保护 确保initialized的原子性atomic.StoreUint32(initialized, 1)}return dbPoolIns
}var once sync.Oncefunc GetDBPool2() *DBPool {once.Do(func() {dbPoolIns DBPool{}})return dbPoolIns
}
func main() {}三、工厂模式
工厂模式Factory Pattern 工厂模式是一种创建型设计模式它提供了一个抽象工厂接口来创建一系列相关的对象而无需指定具体的类。在Go语言中工厂模式通常使用接口和结构体的组合来实现从而实现对不同对象的创建。通过该模式我们可以更加灵活地创建对象并且可以方便地扩展对象的种类。 在这个示例中我们定义了一个名为“Book”的接口该接口有一个名为“Name”的方法。然后我们定义了三个具体的书籍类型ChineseBook、MathBook和EnglishBook并让它们都实现了“Book”接口的“Name”方法。
接下来我们定义了一个名为“GetBook”的函数该函数接受一个字符串参数“name”并根据名称返回一个具体的书籍类型。我们使用一个简单的 switch 语句来实现这个功能并在默认情况下返回 nil。
最后在“main”函数中我们调用“GetBook”函数传递不同的书籍名称并打印出返回的书籍类型。
工厂模式的好处是在增加新的书籍类型时我们只需要添加新的具体类型和一个对应的 case 语句而不需要修改现有的代码。这使得我们的代码更加灵活和可扩展。
在实际应用中工厂模式可以帮助我们更好地组织和管理代码并将复杂的对象创建过程封装起来使得我们的代码更加易于维护和扩展。
package mainimport fmt/*
在小明的学校每一年开学都会发教材
主要包括语文书、数学书、英语书还有各种练习试卷。
这一天小明去领了三本教材分别是语文书、数学书和英语书老师忙不过来指定某个同学去发书
同学们都去这个同学这里去领书。这个同学就是工厂。
*/
type Book interface {Name() string
}
type chineseBook struct {name string
}func (cb *chineseBook) Name() string {return cb.name
}type mathBook struct {name string
}func (mb *mathBook) Name() string {return mb.name
}type englishBook struct {name string
}func (eb *englishBook) Name() string {return eb.name
}
func GetBook(name string) Book {switch name {case 语文书:return chineseBook{name: name}case 数学书:return mathBook{name: name}case 英语书:return englishBook{name: name}default:return nil}
}
func main() {fmt.Println(GetBook(语文书))fmt.Println(GetBook(数学书))
}四、责任链模式
责任链模式Chain of Responsibility Pattern 责任链模式是一种行为型设计模式它将一系列对象连接在一起形成一个责任链。当请求被发送到该链上时每个对象都有机会处理请求或将其传递给下一个对象直到请求被处理为止。在Go语言中责任链模式通常使用链表或者数组来实现从而实现对请求的处理。通过该模式我们可以更加灵活地处理请求并且可以方便地扩展处理的对象。 在这个示例中我们可以将 Assigner 接口看做一个处理器对象用来处理请求并返回相应的书籍或文献。在 assigner 结构体中我们实现了 GetBook 和 GetPaper 方法来获取不同类型的书籍或文献。在 chineseBookAssigner 结构体中我们仅仅处理了语文书籍的请求而其他类型的请求则会被忽略。这样如果一个请求需要被处理它就会被传递给第一个处理器对象然后沿着处理链一直传递到最后一个处理器对象直到找到能够处理请求的处理器对象或者到达处理链的末端。
在 main 函数中我们创建了一个 chineseBookAssigner 对象并调用其 GetBook 方法来获取语文书籍的对象。由于 chineseBookAssigner 只处理语文书籍的请求所以我们可以看到它成功地返回了一个 chineseBook 对象。而对于其他类型的请求则会返回 nil。这样我们就实现了一个简单的责任链模式。
package mainimport fmt/*
在小明的学校每一年开学都会发教材
主要包括语文书、数学书、英语书还有各种练习试卷。
这一天小明去领了三本教材分别是语文书、数学书和英语书老师忙不过来指定某个同学去发书
同学们都去这个同学这里去领书。这个同学就是工厂。
*/
type Book interface {Name() string
}
type Paper interface {Name() string
}type chineseBook struct {name string
}
type chinesePaper struct {name string
}func (cb *chineseBook) Name() string {return cb.name
}type mathBook struct {name string
}func (mb *mathBook) Name() string {return mb.name
}type englishBook struct {name string
}func (eb *englishBook) Name() string {return eb.name
}type Person struct{}type Assigner interface {GetBook(name string) BookGetpaper(string) Paper
}
type assigner struct{}func (a *assigner) GetBook(name string) Book {switch name {case 语文书:return chineseBook{name: name}case 数学书:return mathBook{name: name}case 英语书:return englishBook{name: name}default:return nil}
}type chineseBookAssigner struct {
}func (cba *chineseBookAssigner) GetBook(name string) Book {if name 语文书 {return chineseBook{name: name}}return nil
}
func main() {var a chineseBookAssignerfmt.Println(a.GetBook(语文书))fmt.Println(a.GetBook(数学书))
}