Go 语言面对对象

2022/10/24

Go 语言的语法非常简洁,没有常见的面对对象的语法支持,但是也可以通过现有语法来实现面对对象。

1. Object

在 Go 中类可以通过 struct 配合 method 来实现

duck

在这个例子中一个鸭子类有 color, type 两个属性,有 fly, quack 两个方法,下面是具体的代码实现

type Duck struct {
    duckType  string
    duckColor string
}

func (duck *Duck) fly() {
    fmt.Println("I want to fly higher !")
}

func (duck *Duck) quack() {
    fmt.Println("quack quack ! ! !")
}

func main(){
    duck := Duck{
        duckColor: "black",
        duckType: "mallard",
    }
    duck.fly()
    duck.quack()
}

类中元素的隐藏可以通过元素名称大小写来控制,如果是大写则可以在包外引用如果是小写则只能在包中使用

2. Inheritance

Go 语言并不支持直接继承,但是可以通过 组合(Composition) 的方式来达到相同的目的。比如这个例子,一台电脑可以通过 CPU,RAM,主板来组成,

package computer

import "fmt"

type CPU struct {
    architecture string
}

func (cpu *CPU) SetArchitecture(arch string) {
    cpu.architecture = arch
}

func (cpu *CPU) GetArchitecture() string {
    return cpu.architecture
}

type RAM struct {
    size int
}

func (ram *RAM) SetSize(size int) {
    ram.size = size
}

func (ram *RAM) GetSize() int {
    return ram.size
}

type Motherboard struct {
    category string
}

func (m *Motherboard) SetCategory(cat string) {
    m.category = cat
}

func (m *Motherboard) GetCategory() string {
    return m.category
}

type Computer struct {
    cpu    CPU
    ram    RAM
    mboard Motherboard
}

func (c *Computer) SetSpecification(cpu CPU, ram RAM, mboard Motherboard) {
    c.cpu.SetArchitecture(cpu.GetArchitecture())
    c.ram.SetSize(ram.GetSize())
    c.mboard.SetCategory(mboard.GetCategory())
}

func (c *Computer) ShowSpecification() {
    fmt.Println("CPU: ", c.cpu.GetArchitecture(), ", RAM: ", c.ram.GetSize(), "GB, Motherboard: ", c.mboard.GetCategory())
}

3. polymorphism

Go 中多态用两种实现方法,一种是通过 interface 来实现,比如下面这个例子 GetArea 接受一个 Shape 类型的结构,因为这里 RectangleCircle 都实现了 area 所以他们两者都可以作为参数传给 GetArea

package polymorph

import (
    "fmt"
    "math"
)

type Shape interface {
    area()
}

type Rectangle struct {
    X1, Y1, X2, Y2 float64
}

type Circle struct {
    Xc, Yc, Radius float64
}

func (r *Rectangle) area() {
    fmt.Println("Rectangle Area : ", (r.X2-r.X1)*(r.Y2-r.Y1))
}

func (c *Circle) area() {
    fmt.Println("Circle Area: ", math.Pi*math.Pow(c.Radius, 2))
}

func GetArea(s Shape) {
    s.area()
}

还用一种是通过泛型( generics )来实现,这个是在1.18版本来推出的。比如下面这个例子,他可以同时计算 int64 类型与float64类型的切片和

package main

func sumIntOrFloats[V int64 | float64](vc []V) V {
    var s V
    for _, v := range vc{
        s += v
    }
    return s
}

func main(){
    a := []int64{1, 2, 3}
    r := sumIntOrFloats(a)
    println(r)
}

两种多态的区别在于通过 interface 实现的多态是动态多态,是在运行的时候才确定的,类似与 C++ 的动态调用 ( dynamic dispatch );而通过泛型实现的多态是静态多态,是在编译的时候就确定了,类似与 C++ 中的 template