第一章:Go 语言基础
frp 工具采用 Go 语言开发。个人认为有以下两方面的考虑:
- Go 语言语法简单,易于学习;
- Go 语言中可以轻易实现高效的并发编程。
Example
对于 Java/C++,开发者不仅需要维护线程池,包装一个个的任务,还需要自己去调度线程执行任务并进行上下文切换。相比之下,通过goroutine
机制,Go 程序会智能地将goroutine
中的任务合理地分配给每个 CPU。Go 语言之所以被称为现代化的编程语言,就是因为它在语言层面已经内置了调度和上下文切换的机制。
本章中,我们将从面向对象编程的角度出发,介绍 Go 语言的基础知识。
Go 语言简介
Go(又称 Golang)是 Google 开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。
Go 语言中的面向对象
Go 语言是面向对象的编程语言吗?官方回答:“是也不是”。
Is Go an object-oriented language?
Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).
Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.
Jaana Dogan 在《The Go type system for newcomers》一文中给出的观点是: Go is considered as an object-oriented language even though it lacks type hierarchy, 即“Go被认为是一种面向对象的语言,即使它缺少类型层次结构”。
尽管 Go 语言没有类和继承的概念,但是通过结构体和接口的组合,依然以自己的方式实现了 面向对象编程中封装、继承和多态的特性。
封装
Go 语言中没有class
类型,但是可以通过struct
类型实现封装,
同时,我们也可以为struct
类型定义方法(method)。
下面例子中定义了一个名为Point
的struct
类型,它拥有两个字段x
和y
,以及一个导出
方法Length()
:
package main
// 定义了一个 Point 类型
type Point struct {
x, y float64
}
// 定义了一个方法 Length(),它属于 Point 类型
func (p Point) Length() float64 {
return math.Sqrt(p.x * p.x + p.y * p.y)
}
func main() {
// 创建一个 Point 类型的实例,并调用 Length() 方法
p := Point{x: 3, y: 4}
fmt.Println(p.Length())
}
与经典面向对象语言声明类的方法不同,Go 方法声明并不需要放在声明struct
型的大括号中。
Length()
方法与Point
类型建立联系的纽带是一个被称为receiver
参数的语法元素。
继承
Go 语言中没有继承,但是可以通过类型嵌入(type embedding
)实现类似继承的功能。
下面例子中定义了一个名为ColoredPoint
的struct
类型,它嵌入了Point
类型:
package main
import (
"fmt"
"math"
)
// 定义 Point 结构体
type Point struct {
x, y float64
}
// 为 Point 定义一个方法 Length
func (p Point) Length() float64 {
return math.Sqrt(p.x*p.x + p.y*p.y)
}
// 定义 ColoredPoint 结构体,嵌入 Point
type ColoredPoint struct {
Point
Color string
}
func main() {
// 创建一个 ColoredPoint 实例
cp := ColoredPoint{
Point: Point{x: 3, y: 4},
Color: "red",
}
// 访问嵌入的 Point 的字段和方法
fmt.Println("ColoredPoint coordinates:", cp.x, cp.y)
fmt.Println("ColoredPoint color:", cp.Color)
fmt.Println("Length from origin:", cp.Length())
}
通过这种方式,ColoredPoint
结构体可以使用Point
结构体的所有字段和方法,实现了类似继承的功能。
不过实际 Go 中的这种“继承”机制并非经典面向对象语言中的继承,而是一种组合的方式。
因为ColoredPoint
类型并不是Point
类型的子类型,它只是包含了Point
类型的字段和方法。
多态
经典面向对象的多态实现依托的是类型的层次关系。Go 使用接口(interface
)来解锁多态。
下面例子中定义了一个名为Shape
的接口,它包含一个Area()
方法:
package main
import (
"fmt"
"math"
)
// 定义 Shape 接口
type Shape interface {
Area() float64
}
// 定义 Circle 结构体
type Circle struct {
radius float64
}
// Circle 实现 Shape 接口的 Area 方法
func (c Circle) Area() float64 {
return math.Pi * c.radius * c.radius
}
// 定义 Rectangle 结构体
type Rectangle struct {
width, height float64
}
// Rectangle 实现 Shape 接口的 Area 方法
func (r Rectangle) Area() float64 {
return r.width * r.height
}
// 定义一个函数,接受 Shape 接口类型的参数
func PrintArea(s Shape) {
fmt.Println("Area:", s.Area())
}
func main() {
c := Circle{radius: 5}
r := Rectangle{width: 3, height: 4}
// 调用 PrintArea 函数,传入不同的 Shape 实现
PrintArea(c) // 输出圆的面积
PrintArea(r) // 输出矩形的面积
}
通过这种方式,PrintArea()
函数可以接受任何实现了Shape
接口的类型,从而实现多态。
这展示了 Go 语言如何通过接口实现多态,而不需要类型的层次关系。