• Go语言接口简介

    Go 也有 接口interface )的概念,它是一种特殊的类型,定义了一系列方法签名。其他任意类型,只要实现了接口声明的所有方法,就可以被视为接口类型,赋值给接口变量。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
    package main
    
    import (
    	"fmt"
    	"math"
    )
    
    type Abser interface {
    	Abs() float64
    }
    
    type MyFloat float64
    
    func (f MyFloat) Abs() float64 {
    	if f < 0 {
    		return float64(-f)
    
    	}
    
    	return float64(f)
    }
    
    type Vertex struct {
    	X, Y float64
    }
    
    func (v *Vertex) Abs() float64 {
    	return math.Sqrt(v.X*v.X + v.Y*v.Y)
    }
    
    func main() {
    	var a Abser
    	f := MyFloat(-math.Sqrt2)
    	v := Vertex{3, 4}
    
    	a = f  // a MyFloat implements Abser
    	a = &v // a *Vertex implements Abser
    
    	// In the following line, v is a Vertex (not *Vertex)
    	// and does NOT implement Abser.
    	a = v
    
    	fmt.Println(a.Abs())
    }
    

    上面这个例子,MyFloat*Vertex 都实现了接口 Abser ,因此可以被赋值给 Abser 类型的变量。

    阅读全文
  • Go语言常用接口简介

    Go 语言内置了不少接口定义,掌握这些接口用法可以写出更有 Go 范儿的程序!本节就带领大家,饱览最常用的几个:Stringererror 以及 Reader 等等。

    Stringer

    最常用的接口应该是 fmt 包里的 Stringer

    1
    2
    3
    
    type Stringer interface {
        String() string
    }
    

    满足 Stringer 接口的数据类型,可以用一个字符串来描述自己,fmt 包依赖该接口打印数据。

    阅读全文
  • Go语言泛型机制入门

    泛型函数

    通过 类型参数Go 语言能够编写可以处理多种类型的函数。函数的类型参数位于参数列表前,以中括号括起来:

    1
    
    func Index[T comparable](s []T, x T) int
    

    这个申明表示:s 是任意类型 T 的切片,x 是类型 T ,而 T 是一种内建约束 comparable ,代表任意可比较类型。

    comparable 是一种很有用的类型约束,满足这种约束的类型 T 可以使用 ==!= 运算符进行比较。下面这个例子,我们通过逐一比较,从切片元素中找出指定值。通过类型参数 TIndex 函数可以支持任意可比较类型。

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    package main
    
    import "fmt"
    
    // Index returns the index of x in s, or -1 if not found.
    func Index[T comparable](s []T, x T) int {
        for i, v := range s {
            // v and x are type T, which has the comparable
            // constraint, so we can use == here.
            if v == x {
                return i
            }
        }
        return -1
    }
    
    func main() {
        // Index works on a slice of ints
        si := []int{10, 20, 15, -10}
        fmt.Println(Index(si, 15))
    
        // Index also works on a slice of strings
        ss := []string{"foo", "bar", "baz"}
        fmt.Println(Index(ss, "hello"))
    }
    
    阅读全文
  • Go语言并发处理机制

    Goroutine

    Go 语言提供了 goroutine 用于实现并发,这是一种轻量级线程,有时也叫做 协程

    1
    
    go f(x, y, z)
    

    这行代码启动了一个新协程来执行函数调用:

    1
    
    f(x, y, z)
    

    请注意,函数参数 xyz 求值由当前协程负责;而函数 f 的执行由新协程负责。

    阅读全文
  • Go语言程序开发小技巧

    类型参数推导 引入类型参数后,Go 可以编写泛型函数,来处理不同类型的数据。举个例子,MapKeys 函数可以获取任意 map 键列表: 1 2 3 4 5 6 7 func MapKeys[Key any, Value any, Map map[Key]Data](m Map) []Key { keys := make([]Key, 0, len(m)) for key, _ := range m { keys = append(keys, key) } return keys } 调用泛型函数时,如果类型参数可以根据函数参数推导出来,则无需指定: 1 2 var m map[string]string MapKeys(m) 有时,我们需要绑定类型参数,得到一个特化的版本,例如: 1 var StringMapKeys = MapKeys(string, string, map[string]string) 实际上,第三个类型参数可以根据前两个推导出来,因此我们可以这样简化: 1 var StringMapKeys = MapKeys(string, string) 实际上,泛型函数类型参数位置排列也是有技巧的,类型信息丰富的参数应该排前面:
    阅读全文
  • 利用Go语言泛型特性开发通用算法库(ForEach, Filter, Map, Reduce)

    我们开发程序时,经常要对数据进行各种处理。如果用数学语言对数据处理操作进行抽象,可以归纳成以下操作:

    • AnyMatch ,判断是否有数据满足判定条件;
    • AllMatch ,判断是否所有数据均满足判定条件;
    • ForEach ,遍历每个数据并执行指定处理函数;
    • Filter ,过滤出满足判定条件的数据;
    • Map ,根据指定转换函数逐个转换数据;
    • Reduce ,对数据进行合并,最终计算出一个结果(比如累加);
    • etc
    阅读全文
  • 发布Go语言模块

    开发 Go 程序,我们经常引用第三方模块。那么,怎么发布自己编写的模块呢?

    请注意,一个版本打过标签发布后就不能再修改了。因为 Go 工具链用第一个下载副本来做验证,两个副本不同,就会报安全错误。与其修改先前发布版本的代码,不如发布一个新版本。

    代码仓库

    Go 模块代码通常采用 Git 来管理,代码仓库可以直接托管到 Github 上。为了演示 Go 模块的发布步骤,我在 Github 上建了一个极小化模块:fasionchan/goutils

    模块初始化

    代码仓库建好后还空空如也,我们先执行 go mod init 命令进行初始化(init 后面的参数是模块名):

    1
    
    go mod init github.com/fasionchan/goutils
    

    该命令在当前目录下创建了 go.mod 文件,用来保存模块元数据,内容大致如下:

    1
    2
    3
    
    module github.com/fasionchan/goutils
    
    go 1.19
    
    阅读全文
  • 用Go语言模板引擎提取数据

    最近在开发一个通用消息通知工具,可以从 API 、数据库等数据源获取数据,然后根据数据渲染消息模板,然后通过企微、邮件、短信、电话语音等渠道推送消息。整个工作的设计思想,就是想提供一种低代码、配置化的消息通知解决方案。

    简言之,工具对日常消息通知开发场景进行抽象建模,让所有要素都支持配置:

    • 数据源
    • 消息模板
    • 通知渠道
    • 通知对象
    • 发送策略(手动触发或定时任务)

    那在开发的过程中,就不可避免要面对数据提取问题。比如,有个系统提需求说他们的变更单需要每天催一次,列表通过 API 给我,结构如下:

    1
    2
    3
    4
    5
    6
    7
    
    {
        "success": true,
        "data": [
          {"name": "变更①"},
          {"name": "变更②"}
        ]
    }
    
    阅读全文
  • 用go-restful框架写RESTful API

    本文先介绍一些 RESTful 理念 ,并通过一个 KVS 服务演示 RESTful API 的行为。最后以开发 KVS 服务为例,介绍如何使用 go-restful 框架编写 RESTful API

    RESTful 理念

    RESTRepresentational State Transfer 的缩写, 中文翻译是 表现层状态转换 。这种软件构建风格,通过基于 HTTP 之上的一组 约束属性 ,提供万维网网络服务。其主要特点包括:

    • 统一接口Uniform Interface ),资源通过一致、可预见的 URI 及请求方法来操作;
    • 无状态Stateless ),请求状态由客户端维护,服务端不做保存;
    • 可缓存Cacheable ),可以通过缓存提升服务性能;
    • 分层系统Layered System );

    统一接口RESTful 服务最大的特点。统一接口的核心思想是,将服务抽象成各种 资源 ,并通过一套一致、可预见的 URI 以及 请求方法Request Method )来操作这些资源。这样一来,掌握了一种资源的使用方法,便可延伸到其他资源上,达到举一反三的效果。

    阅读全文
  • Docker化部署Go程序

    Go 语言应用部署 不需要依赖 ,非常简便,这是一个不小的优势。

    Go 语言官方镜像非常大,超过 500MB 。镜像之所以如此庞大是因为它包含了构建 Go 程序所需的全部 工具链 。然而运行编译好的(静态)二进制程序,并不需要这些工具。

    本文介绍如何制作一个紧凑的 Docker 镜像用于部署 Go 应用,大小控制在 10MB 以内。

    本文实验所有操作均在 macOS 下进行,在其他平台进行也是类似的。

    阅读全文