前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Go开源库、大项目的公共包,是这么用建造者模式的

Go开源库、大项目的公共包,是这么用建造者模式的

作者头像
KevinYan
发布2022-10-27 10:51:56
4030
发布2022-10-27 10:51:56
举报
文章被收录于专栏:网管叨bi叨网管叨bi叨

建造者模式,也有翻译成生成器模式的,大家看到后知道他们是一个东西,都是Builer Pattern翻译过来的就行。它是一种对象构建模式,是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 那么什么情况下适合使用建造模式呢?

  • 当要构建的对象很大并且需要多个步骤时,使用构建器模式,有助于减小构造函数的大小。

我们先来看下其他语言里的 Builder,再看看 Go 怎么使用,进行个对比。

Java 的Builder

如果你是写过Java程序一定对下面这类代码很熟悉。

代码语言:javascript
复制
Coffee.builder().name("Latti").price("30").build()

当然,自己给Coffee类加上构建模式,还是需要写不少额外的代码,得给 Coffee 类加一个静态内部类 CoffeeBuilder,用CoffeeBuilder,去建造Coffee类的对象。

类、静态内部类傻傻分不清?可以看下小弟的 Java 文 光会面向对象基础做不了项目,还得掌握这些进阶知识

不过Java里有一个lombok包,只要引入这个包再在实体类加上@Builder注解,就可以使用建造模式构建对象啦。

代码语言:javascript
复制
import lombok.Builder;

@Builder
public class Coffee extends BaseEntity implements Serializable {
    private String name;

    private Long price;
  
    ......
}

Go 里使用Builder

那在Go里面要怎么实现Builder模式呢?仿照上面这个模式,我们可以这样:

假设我们要在项目里搞个 DB 链接池,连接池提供了很多配置化的参数。

代码语言:javascript
复制
type DBPool struct {
    dsn             string
    maxOpenConn     int
    maxIdleConn     int
    ...
    maxConnLifeTime time.Duration
}

我们给 DB 连接池加一个建造者模式,这样在设置每个配置化参数的时候就可以对参数进行一步检查,避免直接 new 连接池对象,再给每个属性赋值时都加判断,把每个参数的校验内聚到参数自己的建造者步骤里。

代码语言:javascript
复制
type DBPoolBuilder struct {
    DBPool
    err error
}

func Builder () *DBPoolBuilder {
    b := new(DBPoolBuilder)
    // 设置 DBPool 属性的默认值
    b.DBPool.dsn = "127.0.0.1:3306"
    b.DBPool.maxConnLifeTime = 1 * time.Second
    b.DBPool.maxOpenConn = 30
    return b
}

func (b *DBPoolBuilder) DSN(dsn string) *DBPoolBuilder {
    if b.err != nil {
        return b
    }
    if dsn == "" {
        b.err = fmt.Errorf("invalid dsn, current is %s", dsn)
    }
    
    b.DBPool.dsn = dsn
    return b
}

func (b *DBPoolBuilder) MaxOpenConn(connNum int) *DBPoolBuilder {
    if b.err != nil {
        return b
    }
    if connNum < 1 {
        b.err = fmt.Errorf("invalid MaxOpenConn, current is %d", connNum)
    }
    
    b.DBPool.maxOpenConn = connNum
    return b
}

func (b *DBPoolBuilder) MaxConnLifeTime(lifeTime time.Duration) *DBPoolBuilder {
    if b.err != nil {
        return b
    }
    if lifeTime < 1  * time.Second {
        b.err = fmt.Errorf("connection max life time can not litte than 1 second, current is %v", lifeTime)
    }
    
    b.DBPool.maxConnLifeTime = lifeTime
    return b
}

func (b *DBPoolBuilder) Build() (*DBPool, error) {
    if b.err != nil {
        return nil, b.err
    }
    if b.DBPool.maxOpenConn < b.DBPool.maxIdleConn {
        return nil, fmt.Errorf("max total(%d) cannot < max idle(%d)", b.DBPool.maxOpenConn, b.DBPool.maxIdleConn)
    }
    return &b.DBPool, nil
}

本文的完整源码,已经同步收录到我整理的电子教程里啦,可向我的公众号「网管叨bi叨」发送关键字【设计模式】领取。

给公众号-网管叨bi叨,发送【设计模式】,领电子教程

接下来就可以使用构建模式创造DBPool类型的对象了

代码语言:javascript
复制
package main 

import "xxx/dbpool"

func main() {
    dbPool, err := dbpool.Builder().DSN("localhost:3306").MaxOpenConn(50).MaxConnLifeTime(0 * time.Second).Build()
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(dbPool)
}

另外在建造者过程的每个参数步骤里,我们都借用了之前提到的处理 Go Error 的方式,把在外部调用时的错误判断,分散到了每个步骤里。

不想Go 错误处理太臃肿,可以参考这个代码设计

这么一来有从观感上觉得确实比定义一个参数巨多的 DBPool 构造函数要好一点。你觉得呢?

Go 里边还有一个函数时编程风格,利用的是函数的可变参数 (variadic parameters) ,这种编程模式就是 Option 模式,之前的文章 一些实用的编程模式--Options模式 里有提到过,可以比较一下。

总结

本文的完整源码,已经同步收录到我整理的电子教程里啦,可向我的公众号「网管叨bi叨」发送关键字【设计模式】领取,目前建造型的设计模式都已经更完,后面开始持续更新其他设计模式。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2022-10-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 网管叨bi叨 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Java 的Builder
  • Go 里使用Builder
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档