如何让golang提供优雅的构造函数呢?
golang是不支持函数重载的,如果想为结构体实现不同程度的初始化,就需要提供不同的构造函数,如下代码
package main
import "fmt"
type Book struct {
Title string
ISBN string
}
func NewBook(title string) *Book {
return &Book{
Title: title,
}
}
func NewBook2(title,isbn string) *Book{
return &Book{
Title: title,
ISBN: isbn,
}
}
func main() {
book := NewBook("Golang")
fmt.Println(book)
book = NewBook2("Golang","1234")
fmt.Println(book)
}
输出:
&{Golang }
&{Golang 1234}
如果Book有十多个属性,是否给某个字段赋初值,排列组合则需要非常多的NewBookX方法
此时可以使用函数式选项模式,不仅可以给一些默认值,还可以灵活的给需要的字段赋值
package main
import "fmt"
type Book struct {
Title string
ISBN string
}
// 如果book有十多个属性,是否给某个字段赋初值,排列组合则需要非常多的NewBookX方法
// 此时可以使用函数式选项模式,不仅可以给一些默认值,还可以灵活的给需要的字段赋值
// 函数的参数为结构体指针,从而可以就地修改结构体中的字段
type option func(book *Book)
func WithTitle(title string) option{
return func (book *Book){
book.Title = title
}
}
func WithISBN(isbn string) option{
return func (book *Book){
book.ISBN = isbn
}
}
func NewBook(ops ...option) *Book{
// book 可以有默认值
book := &Book{
Title: "Java",
ISBN: "",
}
// 通过每个option,给book相应的字段赋值,由于是指针,所以是就地修改
for _,op := range ops{
op(book)
}
return book
}
func main() {
book = NewBook(WithTitle("Golang"),WithISBN("5678"))
fmt.Println(book)
}
输出:
&{Golang 5678}
按照这种方式实现的构造函数,可以高度灵活的给希望赋值的字段赋值,即使Book结构体新增了字段类型,我们也只需要新增一个WithXXX函数即可,符合设计模式中的开闭原则,此外,NewBook在使用时,各个WithXXX作为参数传递的顺序也完全不用关心,不像常规构造函数一样,传参的顺序必须一一对应。
使用函数式编程的第二个重要场景就是使用Gorm框架查询DB时,我们可以通过函数式选项模式灵活的构造查询条件。
首先来看一个大家最容易想到的和数据库交互的代码
package main
import "gorm.io/gorm"
var db *gorm.DB
func InitDB() {
// 一般会根据读取的配置文件初始化DB实例
}
func GetDB() *gorm.DB {
return db
}
// model
type Book struct {
gorm.Model
Title string `gorm:"column:title"`
ISBN string `gorm:"column:ISBN"`
Category int `gorm:"column:category"`
}
func GetBookById(id int) (Book, error) {
var book Book
db := GetDB()
res := db.Where("id = ?", id).First(&book)
return book, res.Error
}
func GetBookByTitle(title string)(Book,error){
var book Book
db := GetDB()
res := db.Where("title = ?", title).First(&book)
return book,res.Error
}
func GetBookByISBN(ISBN string)(Book,error){
var book Book
db := GetDB()
res := db.Where("ISBN = ?", ISBN).First(&book)
return book,res.Error
}
func GetBookByCategory(category int) ([]Book,error){
var books []Book
db := GetDB()
res := db.Where("category = ?",category).Find(&books)
return books,res.Error
}
// 客户端使用代码
func main(){
InitDB()
_,_ = GetBookByTitle("Golang")
_,_ = GetBookByISBN("1234")
_,_ = GetBookByCategory(1)
}
咋一看,完全没有问题,可以实现我们的诉求,但是,如果Book新增了字段,就需要再新写对应的GetBookByXXX方法,这还不是重点,重点是如果要通过多个条件查询怎么办,如要根据title和category两个字段查询,或者要根据title和ISBN两个字段查询,这样组合起来就需要提供非常多的GetBookByXXXAndXXX方法了,显然是很臃肿的。此时函数式选项模式就又派上用场了。
package main
import "gorm.io/gorm"
var db *gorm.DB
func InitDB() {
// 一般会根据读取的配置文件初始化DB实例
}
func GetDB() *gorm.DB {
return db
}
// model
type Book struct {
gorm.Model
Title string `gorm:"column:title"`
ISBN string `gorm:"column:ISBN"`
Category int `gorm:"column:category"`
}
type option func(db *gorm.DB) *gorm.DB
func WithTitle(title string) option {
return func(db *gorm.DB) *gorm.DB {
db = db.Where("title = ?", title)
return db
}
}
func WithISBN(ISBN string) option {
return func(db *gorm.DB) *gorm.DB {
db = db.Where("ISBN = ?", ISBN)
return db
}
}
func WithCategoryIn(categories []int) option {
return func(db *gorm.DB) *gorm.DB {
db = db.Where("category in (?)", categories)
return db
}
}
// GetBookByOptions 和DB交换获取书籍的函数就都请求这个就行了
func GetBookByOptions(ops ...option) ([]Book, error) {
var books []Book
db := GetDB()
for _, op := range ops {
// 注意gorm中db链式调用后,还需要用db接收
db = op(db)
}
res := db.Find(&books)
return books, res.Error
}
// 客户端使用代码
func main() {
InitDB()
_,_ = GetBookByOptions(WithTitle("Golang"),WithCategoryIn([]int{1,2,3}))
}
对于DB查询,工作中还有一种非常常见的写法,那就是定义一个结构体作为参数,根据字段的内容构造SQL,如下:
package main
import "gorm.io/gorm"
var db *gorm.DB
func InitDB() {
// 一般会根据读取的配置文件初始化DB实例
}
func GetDB() *gorm.DB {
return db
}
// model
type Book struct {
gorm.Model
Title string `gorm:"column:title"`
ISBN string `gorm:"column:ISBN"`
Category int `gorm:"column:category"`
}
// GetBookByOptions 和DB交换获取书籍的函数就都请求这个就行了
func GetBooks(book *Book) ([]Book, error) {
var books []Book
db := GetDB()
if book.Title != "" {
db = db.Where("title = ?", book.Title)
}
if book.ISBN != "" {
db = db.Where("ISBN = ?", book.ISBN)
}
if book.Category > 0 {
db = db.Where("category in (?)", book.Category)
}
res := db.Find(&books)
return books, res.Error
}
// 客户端使用代码
func main() {
InitDB()
book := &Book{
Title: "Golang",
}
_, _ = GetBooks(book)
}
Golang中的函数式构造函数与DB查询优化
1万+

被折叠的 条评论
为什么被折叠?



