14.面向对象编程

予早 2024-12-19 23:58:29
Categories: Tags:

Go中没有面向对象的概念,若期望以面向对象的思路进行编程,需要基于结构体struct进行。

由于Go不支持OOP,故:

  1. Go中无专门class体系
  2. Go中无方法重载
  3. Go中无构造函数、析构函数
  4. Go中无this或self指向本身的指针

基于struct的伪OOP

结构体基本特征

  1. 结构体是值类型
  2. 结构体内存连续
package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func main() {
    //结构体变量
    var p1 Person
    fmt.Println(p1)
    var p2 Person = Person{
        Name: "",
        Age:  0,
    }
    fmt.Println(p2)

    //结构体指针
    var p3 *Person = new(Person)
    fmt.Println(p3)
    var p4 *Person = &Person{}
    fmt.Println(p4)

    //不同于C语言中s.name s->name的使用方式
    //Go中只有s.name方式,即使是指针,也是(*s).name的方式
    //但是Go编译器对(*s).name进行了优化,允许指针s.name的写法,但实际上仍是指针的效果

}

结构体强转

package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

type Student struct {
    Name string
    Age  int
}

func main() {
    var p Person

    var s Student = Student{ //也可以{"张三", 18}
        Name: "张三",
        Age:  18,
    }

    //结构体名字或者字段不一样即是不同类型的结构体,不同数据类型
    //如果要强转,只能名字不一样,即要求结构体字段名称或者类型完全一样,包括字段顺序,即实质一致,要求内存分布一致
    p = Person(s)
    fmt.Println(p)

}

结构体tag

结构体tag,由反射机制处理

package main

import (
    "encoding/json"
    "fmt"
)

type Student struct {
    Name string `json:"name,omitempty"`
    Age  int    `json:"age,omitempty"`
}

func main() {

    var s Student = Student{
        Name: "张三",
        Age:  18,
    }

    marshal, err := json.Marshal(s)
    if err != nil {

    }

    //私有属性不能参与序列化
    fmt.Println(string(marshal))
    fmt.Println(marshal)

}

方法

func (receiver type) methodName (参数列表) (返回值列表) {
    方法体
    return
}

//1.receiver type表示方法与该类型绑定,即该类型变量可以调用该方法
//2.receiver type可以是结构体,也可以是其他类型,如int,不过好像不能直接用int
//3.return 非必须
//4.!!!建议receiver
  1. 方法和指定数据类型绑定,任意自定义类型都可以有方法,如int、float64,并非仅仅struct,这一点方法的含义更加宽泛

    type A struct {
        Num int
    }
    
    func (a A) test() { //a A相当于融合了绑定关系和self或this,但又不仅仅局限于结构体,a名称任意
        fmt.Println(a.Num)
    }
    
  2. 方法同样适用大小写风格访问控制权限

  3. 如果某类型实现String方法,则fmt.Println默认使用该方法进行输出

  4. 根据指针简化原则,需要指针调用的方法和值调用的方法都可以传入指针或者值,即接受值类型,可以传入指针类型,接受指针类型,可以传入值类型,我感觉都应该接受指针类型吧

  5. 方法与函数最大的不同在于绑定关系

  6. Go中没有构造函数和析构函数,一般使用工厂模式实现创建对象

  7. 注意:Go中结构体内部字段和方法是可以被包内访问的,不仅仅是在结构体所属方法中,这一点与Java等严密的封装不同

    package main
    
    import "fmt"
    
    type Student struct {
        Name string
        age  int
    }
    
    func main() {
    
        var s = Student{
            Name: "张三",
            age:  18,
        }
    
        fmt.Println(s.Name, s.age) //张三 18
    }
    

构造函数的替代->工厂模式

package main

import (
    "fmt"
    "gocode/testproject01/unit10/demo11/model"
)

func main() {
    // 跨包创建结构体Student的实例:
    // var s model.Student = model.Student{"丽丽", 10}
    // s := model.student{"丽丽", 10}
    // fmt.Println(s)

    s := model.NewStudent("娜娜", 20)
    fmt.Println(s)
}

封装

Go并非OOP严格封装,如不像Java那么严格

  1. 使用大小写风格访问控制权限
  2. 基于工厂模式创建对象,类似构造函数
  3. 使用get、set方法访问属性
  4. 特别注意:如果是在本包中,即使某一个函数与结构体无绑定关系,这个函数也是能访问结构体私有属性的,例如工厂模式实现函数,可能是一种模糊不清的“类静态方法”

继承

Go中基于结构体匿名字段方式实现继承

结构体可以使用匿名结构体属性所有的属性和方法,即包括公有或者私有

结构体的匿名属性可以是基本类型

支持多继承,使用多个匿名结构体属性即可

菱形继承问题必须指定匿名结构体属性

结构体属性可以为结构体类型,组合关系

type A struct {
    a int
    b string
    c B
}
package main

import "fmt"

type Person struct {
    Name string
    Age  int
}

func (p Person) String() string {
    return fmt.Sprintf("Person %s %d", p.Name, p.Age)
}

type Student struct {
    Person //匿名的Person属性 //如果此处为*Person,则下方赋值时需要传地址
    int
}

func main() {

    var s = Student{Person{ //赋值 //&Person{}
        Name: "0",
        Age:  0,
    }, 0}
    fmt.Println(s.Age, s.Person.Age)
    s.Age = 1
    fmt.Println(s.Age, s.Person.Age)
    fmt.Println(s.String())
}

多态

多态

Go的多态是基于接口实现的

//基于多态的数组
var arr [3]SayInterface
arr[0] = American{"rose"}
arr[1] = Chinese{"张三"}
arr[2] = Chinese{"李四"}

接口

Go中的接口是一种协议接口,即若A接口包含B、C方法描述,而D类型实现了接口A的所有方法,即B、C方法,则可以说D实现了接口A

只要是自定义数据类型就可以实现接口,并不一定是struct

一个自定义类型可以实现多个接口

接口是引用类型,默认值为nil

空接口,空接口没有任何方法,所有类型都实现了空接口,即空接口可以指向任意类型变量

type 接口名称 interface {
    method1(参数列表) 返回值列表
    method1(参数列表) 返回值列表
    ...
}
package main

import "fmt"

type SayInterface interface {
    Say()
}

func Say(say SayInterface) {
    say.Say()
}

type Chinese struct {
}

func (chinese Chinese) Say() {
    fmt.Println("你好")
}

type American struct {
}

func (american American) Say() {
    fmt.Println("Hello")
}

type Integer int

func (Integer) Say() {
    //1.绑定类型可以省略名字,但这样就无法使用对象本身了
    //2.任意自定义类型就可以实现接口
    fmt.Println(1)
}

func main() {
    c := Chinese{}
    a := American{}
    c.Say()
    a.Say()

    //函数式
    Say(c)
    Say(a)

    var i SayInterface = Integer(1) //多态
    i.Say()

}

接口继承,与结构体继承方式一致,用于接口扩展