go语言使用总结(持续更新)

news/2024/11/13 7:59:29/

整理后的内容如下:


1. 先了解函数签名,再了解传入参数以及调用

  • 函数签名是函数的声明部分,包括函数名、参数列表和返回值列表。理解函数签名是理解函数行为的第一步,尤其是在了解参数类型、参数数量和返回值类型等方面。
  • 通过了解函数签名,可以确定函数在调用时需要什么样的输入(参数类型和数量)以及函数会返回什么样的结果。

示例

func add(a int, b int) int {return a + b
}
  • 函数 add 的签名 func add(a int, b int) int 表示它接收两个 int 类型的参数并返回一个 int 类型的结果。
  • 在使用一个函数前,了解其签名有助于确定如何传递参数和如何处理返回值。

2. 传入参数是某种类型,只要有变量实现了这个类型,那么这个变量就可以作为参数传入

  • 在 Go 语言中,如果一个参数类型是接口类型,只要一个变量实现了该接口中的方法,那么该变量就可以作为这个接口类型的参数传入。
  • 这就是 Go 中的接口的实现方式:只要类型实现了接口的所有方法,那么这个类型自动满足该接口,无需显式声明实现。

示例

type Speaker interface {Speak() string
}type Dog struct{}func (d Dog) Speak() string {return "Woof!"
}func MakeNoise(s Speaker) {fmt.Println(s.Speak())
}func main() {d := Dog{}MakeNoise(d)  // d 是 Dog 类型,但它实现了 Speak 方法,因此满足 Speaker 接口
}
  • Dog 类型实现了 Speaker 接口的方法 Speak,因此可以作为 Speaker 类型的参数传入 MakeNoise 函数。

3. 构造函数返回值一般都是指针类型有什么问题吗

  • 在 Go 中,构造函数返回指针类型(如 *Type)是常见的设计模式。返回指针类型的构造函数有几个优点:
    • 效率:对于大型结构体,返回指针避免了结构体数据的拷贝,提升了效率。
    • 易于修改:返回指针类型允许调用者对返回对象进行修改,而不会影响原有的对象。
    • 方法调用:如果方法是定义在指针类型上(如 func (t *Type) Method()),那么必须返回一个指针,才能调用这些方法。

可能的问题

  • 内存管理:返回指针会在堆上分配内存,可能增加垃圾回收的负担。因此对于小型的不可变结构体,直接返回值类型更合适。
  • 不可变性:指针类型的返回值可以被外部修改,可能导致不安全的状态变更。如果不希望对象被外部修改,返回值类型会更合适。

示例

type Config struct {URL string
}func NewConfig(url string) *Config {return &Config{URL: url}  // 返回指针类型
}func main() {config := NewConfig("http://localhost")fmt.Println(config.URL)  // 直接使用指针调用字段
}

在 Go 中,返回指针类型通常用于允许共享对象状态,但根据实际需求,可以选择返回值类型或指针类型。


4. 使用 Go Modules 的注意点

在使用 Go Modules 时,如果 main.gogeecache/ 在同级目录下,不能再使用 import 相对路径进行模块引用。为了在 Go Modules 中正确引入本地模块,可以在 go.mod 中声明模块替换:

require geecache v0.0.0
replace geecache => ./geecache

通过 replace 指令将 geecache 映射为本地路径 ./geecache,可以在项目中以模块方式正确引用本地包。

这是 Go 语言中的接口实现语法,具体而言,它利用了 隐式接口实现 的特性。

5. 隐式接口实现

在 Go 语言中,如果一个类型(例如 ByteView)实现了某个接口(例如 Value)的所有方法,那么它就自动满足该接口,无需显式声明“实现了”这个接口。这种机制称为 隐式接口实现。因此,在 Go 中,接口是通过结构体的方法集合来判断是否满足接口的,而不是通过显式声明。

示例解释
type Value interface {Len() int
}type ByteView struct {b []byte
}func (v ByteView) Len() int {return len(v.b)
}

在上面的代码中:

  • Value 接口定义了一个 Len() 方法。
  • ByteView 类型实现了 Len() 方法,因此满足了 Value 接口的要求。
  • Go 编译器会自动将 ByteView 视为 Value 类型,因为它实现了接口所要求的全部方法。
接口变量的多态性

因为 ByteView 实现了 Value 接口,所以可以将 ByteView 的实例赋值给一个 Value 类型的变量。这种用法允许 Go 语言的接口具有多态性

var v Value
v = ByteView{b: []byte("Hello, World!")}
fmt.Println(v.Len())  // 输出: 13

在这里:

  • v 是一个 Value 类型的接口变量,可以接受任何满足 Value 接口的类型。
  • ByteView 实现了 Len() 方法,因此可以赋值给 v
  • 调用 v.Len() 时,实际上会调用 ByteView.Len() 方法。
接口解耦

Go 的接口实现方式使得代码的模块化和解耦更加灵活:

  • Cache 可以依赖 Value 接口,而不是依赖具体的 ByteView 类型。
  • 这样一来,只要其他类型实现了 Len() 方法,也可以被 Cache 使用,具有更好的扩展性。

6. 函数式接口作用

函数式接口在 Go 中的意义主要体现在简化代码结构增强代码灵活性提高复用性方面。Go 语言中的函数式接口通常是通过类型别名实现的,允许将函数作为参数传递,使得代码更具模块化和扩展性。以下是函数式接口的核心意义和应用场景:

提高代码的灵活性

  • 函数式接口使得代码可以接受行为(函数)作为参数,这样可以根据上下文动态地改变函数的行为,而不需要创建大量的具体实现。
  • 例如,假设有一个缓存系统,需要从数据源加载数据,可以通过函数式接口将数据源的加载逻辑作为参数传入,从而实现不同数据源的适配。

示例

type LoaderFunc func(key string) ([]byte, error)func LoadData(loader LoaderFunc, key string) ([]byte, error) {return loader(key)
}

在使用时,可以传入不同的 LoaderFunc 实现,使 LoadData 能够适配不同的加载逻辑。

减少冗余代码

  • 函数式接口避免了为简单的功能创建单独的结构体和实现方法。对于那些功能性较强但实现相对简单的行为,函数式接口能让代码更加简洁。
  • 在没有函数式接口的情况下,开发者可能需要为每个行为编写一个结构体实现接口,但函数式接口可以避免这种代码膨胀。

增强代码的复用性

  • 使用函数式接口,可以将通用的逻辑和策略抽象出来,赋予代码更高的复用性。例如,数据处理、过滤器、回调处理等场景都可以利用函数式接口,使通用的逻辑可以在不同场景下复用。
  • 例如,可以定义一个泛型的排序逻辑,接受一个比较函数,实现不同的排序策略。

示例

type CompareFunc func(a, b int) boolfunc Sort(data []int, compare CompareFunc) {// 使用 compare 函数进行排序
}

简化依赖注入和测试

  • 函数式接口使得依赖注入更为简便,例如在测试时,可以传入特定的函数来替换真实的服务。这在需要模拟特定行为、简化测试环境设置时特别有用。
  • 例如,可以用一个简单的函数替换数据库查询,使测试代码不依赖于实际的数据库连接。

示例

func MockLoader(key string) ([]byte, error) {return []byte("mock data"), nil
}data, _ := LoadData(MockLoader, "test-key")

http://www.ppmy.cn/news/1545932.html

相关文章

166页PDF | 埃森哲-XX集团企业架构数字化整体规划设计方案(限免下载)

一、前言 这份报告是埃森哲为XX集团制定的企业架构数字化整体规划设计方案,涵盖了业务、应用、数据、技术架构设计以及信息化管控体系的构建。报告详细分析了集团的信息化现状、面临的挑战,并提出了相应的战略目标和管理要求。同时,报告还规…

国产化浪潮下,高科技企业如何选择合适的国产ftp软件方案?

高科技企业在数字化转型和创新发展中,数据资产扮演着越来越重要的角色。在研发过程中产生的实验数据、设计文档、测试结果等,专利、商标、版权之类的创新成果等,随着信息量急剧增加和安全威胁的复杂化,传统的FTP软件已经不能满足这…

SCRM与CRM的区别解析 企业如何选择合适的客户管理工具

内容概要 在客户管理领域,SCRM与CRM这两个缩写大家可能都有所耳闻,但它们的具体含义和应用场景有哪些不同呢?简单来说,CRM(客户关系管理)主要聚焦于销售效率,帮助企业更有效地管理销售过程&…

【数据结构与算法】LRUCache

实现LRUCache LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,否则返回 -1 。void put(int key, int value) 如果关键字 key 已经存在,则变更其数据值…

网络安全常见面试题--含答案

本文面试题汇总: 防范常见的 Web 攻击 重要协议分布层 arp协议的工作原理rip协议是什么?rip的工作原理 什么是RARP?工作原理OSPF协议?OSPF的工作原理 TCP与UDP区别总结 什么是三次握手四次挥手? tcp为什么要三次握手&…

力扣第46题“全排列”

题目描述 给定一个没有重复数字的整数数组 nums,返回其所有可能的排列。 示例: 输入: nums [1,2,3] 输出: [[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1] ]解题思路 回溯法:通过递归构建排列,每次选择一个未使用的数字添…

《数据结构》--二叉树【上】

🌳树的基础内容 树的定义: 树是一种数据结构,它是由n(n>1)个有限节点组成的一个具有层次关系的集合。把它叫做"树"是因为它看起来像一颗倒挂着的树,也就是说它是根在上,叶在下的。 树的特点&#xff1a…

openresty入门教程:init_by_lua_block

init_by_lua_block 是 Nginx 配置中用于在 Nginx 启动时执行 Lua 脚本的一个指令。这个指令通常用于初始化全局变量、设置共享内存,或者执行一些需要在服务器启动时完成的准备工作。 以下是一个简单的 init_by_lua_block 使用示例: 1. 安装 Nginx 和 L…