一. 面向接口
接口的概念
接口的本质是引入一个新的中间层,调用方可以通过接口与具体实现分离,解除上下游的耦合,上层的模块不再需要依赖下层的具体模块,只需要依赖一个约定好的接口
duck typing
- "像鸭子走路,像鸭子叫(长得像鸭子),那么就是鸭子"
- 描述事物的外部行为而非内部结构
- 严格来说go属于结构化类型系统,类似duck typing
- go同时具有python,c++的duck typing的灵活性,又具备java的类型检查
接口的定义和实现
- 接口的实现是隐式的
- 只要实现接口里的方法
package mock
type Retriever struct {
Contents string
}
func (r Retriever) Get(url string) string {
return r.Contents
}
package main
import (
"awesomeProject/retriever/mock"
"fmt"
)
type Retriever interface {
Get(url string) string
}
func download(r Retriever) string {
return r.Get("www.baidu.com")
}
func main() {
var retriever Retriever = mock.Retriever{Contents: "this a test"}
fmt.Println(download(retriever))
}
Retriever结构体就是对接口的一个实现,从类型检查的过程来看,编译器仅在需要时才检查类型,类型实现接口时只需要实现接口中的全部方法,不需要像 Java 等编程语言中一样显式声明。
二. 函数式编程
函数式编程的 & 函数指针
- 函数是一等公民:参数,变量,返回值都可以是函数
- 高阶函数
- 函数 -> 闭包
正统的函数式编程
- 不可变性:不能有状态,只有常量和函数
- 函数只能有一个参数
非常有意思的啊,之前学过的,但是看起来go的函数式编程非常陌生啊
package main
import "fmt"
func adder(sum int) func(int2 int) int {
sum = 0
return func(int2 int) int {
sum += int2
return sum
}
}
type Aadder func(int) (int, Aadder)
func adder3(base int) Aadder {
return func(int2 int) (int, Aadder) {
return base + int2, adder3(base + int2)
}
}
func Fibo() func() int {
a, b := 0, 1
return func() int {
a, b = b, a+b
return a
}
}
func main() {
f := adder(1)
for i := 0; i < 10; i++ {
fmt.Printf("0 + 1 + ... + %d = %d\n", i, f(i))
}
var a = adder3(0)
for i := 0; i < 10; i++ {
i2, _ := a(i)
fmt.Printf("0 + 1 + ... + %d = %v\n", i, i2)
}
}
三. 错误处理
defer调用
错误处理概念
服务器统一出错处理
四. 测试
Debugging Sucks!
Test Rocks
测试
传统测试
- 测试数据和测试逻辑混在一起
- 出错信息不明确
- 一旦一个数据出错测试全部结束
表格驱动测试
- 分离的测试数据和测试逻辑
- 明确的出错信息
- 可以部分失败
- go语言的语法使得我们更容易实践表格驱动测试
package main
import "math"
func triangle(a, b int) int {
return int(math.Sqrt(float64(a*a + b*b)))
}
func main() {
}
package main
import (
"testing"
)
func TestTriangle(t *testing.T) {
tests := []struct{ a, b, c int }{{3, 4, 5}, {1, 4, 5}}
for _, tt := range tests {
if actual := triangle(tt.a, tt.b); actual != tt.c {
t.Errorf("%d , %d ,%d , %d", tt.a, tt.b, actual, tt.c)
}
}
}
Bug解决:previous declaration,同一个目录下面不能有个多 package main
test测试可以使用命令行 go test .
代码覆盖率和性能测试
使用pprof进行性能调优
func BenchmarkStr(b *testing.B) {
s := "小米小红小王"
for i := 0; i < 20; i++ {
s = s + s
}
b.Logf("len(s)=%d", len(s))
b.ResetTimer()
ans := 8
for i := 0; i < b.N; i++ {
i2 := takeNum(s)
if i2 != ans {
b.Errorf("%d", ans)
}
}
}
- -cpuprofile获取性能数据
- go toolpprof 查看性能数据
- 分析慢在哪里
- 优化代码
生成文档和示例代码
- go doc package 获取包的文档注释,例如:go doc fmt 会显示使用 godoc 生成的 fmt 包的文档注释。
- go doc package/subpackage 获取子包的文档注释,例如:go doc container/list。
- go doc package function 获取某个函数在某个包中的文档注释,例如:go doc fmt Printf 会显示有关 fmt.Printf() 的使用说明
五. goroutine
package main
import (
"fmt"
"time"
)
func main() {
for i := 0; i < 1000; i++ {
go func(i int) {
for {
fmt.Printf("hello world %d\n", i)
}
}(i)
}
time.Sleep(time.Millisecond)
}
- 轻量级线程
- 非抢占式多任务,由协程主动交出控制权
- 编译器/解释器/虚拟机层面的多任务
- 多个携程可能在一个或多个线程运行
package main
import (
"fmt"
"runtime"
"time"
)
func main() {
var a = [10]int{}
for i := 0; i < 10; i++ {
go func(i int) {
for {
a[i]++
runtime.Gosched()
}
}(i)
}
time.Sleep(time.Millisecond)
for i := range a {
fmt.Println(a[i])
}
}
goroutine
- 任何函数只需要加上go就能送给调度器运行
- 不需要在定义时区区分是否是异步函数
- 调度器在合适的点进行切换
- 使用-race来检测数据访问冲突
六. channel❤️
属是有点复杂...改天在学吧.....
并发模式
Select
七. 标准库
http
package main
import (
"fmt"
"net/http"
"net/http/httputil"
)
func main() {
newRequest, err := http.NewRequest(http.MethodGet, "https://www.baidu.com", nil)
newRequest.Header.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36 Edg/103.0.1264.49")
response, err := http.DefaultClient.Do(newRequest)
//response, err := http.Get("https://www.baidu.com")
if err != nil {
panic(err)
}
defer response.Body.Close()
bytes, err := httputil.DumpResponse(response, true)
if err != nil {
panic(err)
}
fmt.Printf("%s\n", bytes)
}
http服务器的性能分析
json
package main
import (
"encoding/json"
"fmt"
)
// tag的使用
type Order struct {
ID string `json:"id"`
Name string
Quantity int
Price float64 `json:"price,omitempty"`
}
type Shops struct {
Add string `json:"add"`
Order Order `json:"order"`
}
type Orders struct {
Order []Order `json:"order"`
}
func main() {
order := Order{
ID: "0001",
Name: "A衣服",
Quantity: 10,
}
shops := Shops{
Add: "x0001",
Order: order,
}
orders := Orders{
Order: []Order{
{
ID: "0001",
Name: "A衣服",
Quantity: 1000,
},
order,
},
}
fmt.Printf("%v\n", order)
fmt.Printf("%+v\n", order)
if order, err := json.Marshal(order); err == nil {
fmt.Printf("%s\n", order)
} else {
panic(err)
}
if shops, err := json.Marshal(shops); err == nil {
fmt.Printf("%s\n", shops)
} else {
panic(err)
}
if orders, err := json.Marshal(orders); err == nil {
fmt.Printf("%s\n", orders)
} else {
panic(err)
}
s := `{"order":[{"id":"0001","Name":"A衣服","Quantity":1000},{"id":"0001","Name":"A衣服","Quantity":10}]}`
var O Orders
err := json.Unmarshal([]byte(s), &O)
if err != nil {
panic(err)
}
fmt.Printf("%+v\n", O)
}
- Json数据格式
- 结构体的tag
- Json Marshal与Unmarshal数据类型
第三方数据格式的解析
比map简单....
package main
import (
"encoding/json"
"fmt"
)
func main() {
s := `{
"result": [{
"id": "0",
"word": "请",
"tags": ["基本词-中文"]
}, {
"id": "1",
"word": "输入",
"tags": ["基本词-中文", "产品类型修饰词"]
}, {
"id": "2",
"word": "文本",
"tags": ["基本词-中文", "产品类型修饰词"]
}]
}`
m := struct {
Result []struct {
Id string `json:"id"`
Tags []string `json:"tags"`
} `json:"result"`
}{}
err := json.Unmarshal([]byte(s), &m)
if err != nil {
panic(err)
}
fmt.Printf("%+v , %+v\n", m.Result[2].Id, m.Result[2].Tags)
}