美文网首页
【go语言学习】面向对象oop

【go语言学习】面向对象oop

作者: Every_dawn | 来源:发表于2020-09-30 18:13 被阅读0次

    go并不是一个纯面向对象的编程语言。在go中的面向对象,结构体替换了类。
    go并没有提供类class,但是它提供了结构体struct,方法method可以在结构体上添加。提供了捆绑数据和方法的行为,这些数据和方法与类类似。

    面向对象的基本思想主要体现在封装,继承以及多态等的设计与运用上。下面来看看封装、继承与多态在golang中是如何实现的。

    一、封装

    • 封装主要是通过访问权限控制实现的。
    • 在Java中,共有public 、protected、default、private这四种权限控制。
    • 而相应的在golang中,是通过约定来实现权限控制的。变量名首字母大写,相当于java中的public,首字母小写,相当于private。同一个包中访问,相当于default。由于go没有继承,也就没有protected。

    在go_project目录下创建model文件,在model文件夹下创建student.go

    package model
    // student 学生结构体,首字母小写,包外不可见
    type student struct {
        Name string //  首字母大写,包外可见
        age  int  // 首字母小写,包外不可见
    }
    
    // New student的构造函数
    func New(name string, age int) *student {
        return &student{
            Name: name,
            age:  age,
        }
    }
    
    // GetAge student的方法,用于外部包访问age字段
    func (s *student) GetAge() int {
        return s.age
    }
    
    // SetAge student的方法,用于外部包修改age字段
    func (s *student) SetAge(age int) {
        s.age = age
    }
    

    在go_project目录下创建main文件夹,在main文件夹下创建main.go

    package main
    
    import (
        "fmt"
        "go_project/model"
    )
    
    func main() {
        s := model.New("tom", 20)
        fmt.Println(s)
        // 可以直接访问Name字段
        fmt.Println(s.Name)
        // 访问不到age字段
        // fmt.Println(s.age)
        fmt.Println(s.GetAge())
        s.Name = "jack"
        // 无法直接修改age字段
        // s.age = 23
        s.SetAge(23)
        fmt.Println(s)
    }
    

    运行结果

    &{tom 20}
    tom
    20
    &{jack 23}
    
    • 在model包里面定义了一个student的结构体;由于首字母小写,所以不可能从其他包中创建student结构体的实例。也就是说没有其他的包能够创建一个零值的student实例。
    • 现在导出student实例只能通过构造函数NewT(parameters)。如果包只定义了一个类型,那么约定将这个函数命名为New(parameters)而不是NewT(parameters)。
    • 由于没有将student的所有字段导出,避免了对student字段的随意访问和修改。只能通过导出的student的GetSet方法操作。

    二、继承

    go不支持继承,可以通过将一个struct类型嵌入到另一个结构中实现类似继承效果。

    package main
    
    import "fmt"
    
    // Person 结构体 父类
    type Person struct {
        name string
        age  int
    }
    
    // Say Person的方法,父类方法
    func (p Person) Say() {
        fmt.Println(p.name, "say hello")
    }
    
    // Student 结构体 子类
    type Student struct {
        Person
        school string
    }
    
    // Study Student的方法, 子类自己的方法
    func (s Student) Study() {
        fmt.Println(s.name, "goood good study, day day up")
    }
    
    // Say Student的方法,重写父类的方法
    func (s Student) Say() {
        fmt.Println(s.name, "说:你好!")
    }
    
    func main() {
        // 创建父类对象
        p := Person{
            name: "tom",
            age:  19,
        }
        p.Say()
        // 创建子类对象
        s := Student{
            Person: Person{
                name: "jack",
                age:  18,
            },
            school: "清华大学",
        }
        // 访问父类的方法
        s.Say()
        // 访问子类自己的方法
        s.Study()
    }
    

    运行结果

    tom say hello
    jack 说:你好!
    jack goood good study, day day up
    

    三、多态

    Java 中的多态是通过 extends class 或者 implements interface 实现的,在 golang 中既没有 extends,也没有 implements ,那么 go 中多态是如何实现的呢 ?

    答案:在golang中,只要某个struct实现了某个interface中的所有方法,那么我们就认为,这个struct实现了这个接口。

    接口类型的变量可以保存实现接口的任何值。接口的这个属性用于实现Go中的多态性。

    一个接口的实现:

    • 看成实现本身的类型,可以访问实现类中的属性和方法
    • 看成对应的接口类型,只能访问接口中的方法
    package main
    
    import "fmt"
    
    // AnimalIF 接口
    type AnimalIF interface {
        Eat()
        Sleep()
    }
    
    // Animal 结构体 父类
    type Animal struct {
        name string
        age  int
    }
    
    // Eat Animal的方法
    func (a Animal) Eat() {
        fmt.Println(a.name, "eat")
    }
    
    // Sleep Animal的方法
    func (a Animal) Sleep() {
        fmt.Println(a.name, "sleep")
    }
    
    // Cat 结构体 子类
    type Cat struct {
        Animal
        color string
    }
    
    // Eat Cat的方法,重写父类的方法
    func (c Cat) Eat() {
        fmt.Println("cat eat fish")
    }
    
    // Dog 结构体 子类
    type Dog struct {
        Animal
    }
    
    // LookDoor Dog的方法, 子类自己的方法
    func (d Dog) LookDoor() {
        fmt.Println("dog lookdoor")
    }
    
    func main() {
        // 创建父类对象
        a := Animal{
            name: "狮子",
            age:  5,
        }
        a.Eat()
        // 创建子类对象
        c := Cat{
            Animal: Animal{
                name: "小花",
                age:  3,
            },
            color: "白色",
        }
        // 创建子类对象
        d := Dog{
            Animal: Animal{
                name: "旺财",
                age:  2,
            },
        }
        // 访问子类拥有的父类的字段
        fmt.Println(c.name, c.age)
        // 访问子类自己的字段
        fmt.Println(c.color)
        // 访问父类的方法
        d.Eat()
        // 访问子类自己的方法
        d.LookDoor()
        // 访问子类重写的父类的方法
        c.Eat()
        fmt.Println("--------------------")
    
        // 创建接口类型
        var ai AnimalIF
        ai = a
        ai.Eat()
        ai.Sleep()
        ai = c
        ai.Eat()
        ai.Sleep()
        ai = d
        ai.Eat()
        ai.Sleep()
    }
    

    运行结果

    狮子 eat
    小花 3
    白色
    旺财 eat
    dog lookdoor
    cat eat fish
    --------------------
    狮子 eat
    狮子 sleep
    cat eat fish
    小花 sleep
    旺财 eat
    旺财 sleep
    

    应用举例
    计算一些图形的面积,目前有正方形和圆形。

    package main
    
    import (
        "fmt"
        "math"
    )
    
    // Area 面积接口,包含两个方法source和calculate
    type Area interface {
        source() string
        calculate() float64
    }
    
    // Square 结构体,正方形
    type Square struct {
        graphName  string
        sideLength float64
    }
    
    // source Square的方法
    func (s Square) source() string {
        return s.graphName
    }
    
    // calculate Square的方法
    func (s Square) calculate() float64 {
        return math.Pow(s.sideLength, 2)
    }
    
    // Round 结构体,圆形
    type Round struct {
        graphName string
        radius    float64
    }
    
    // source Round的方法
    func (r Round) source() string {
        return r.graphName
    }
    
    // calculate Round的方法
    func (r Round) calculate() float64 {
        return math.Pi * math.Pow(r.radius, 2)
    }
    
    // totalArea 获取总面积的函数
    func totalArea(a []Area) {
        var totalArea = 0.0
        for _, v := range a {
            fmt.Printf("面积来自%v, 它的面积是%v\n", v.source(), v.calculate())
            totalArea += v.calculate()
        }
        fmt.Printf("所有图形的总面积是:%v\n", totalArea)
    }
    
    func main() {
        graph1 := Square{
            graphName:  "graph1",
            sideLength: 6.3,
        }
        graph2 := Round{
            graphName: "graph2",
            radius:    3.4,
        }
        a := []Area{graph1, graph2}
        totalArea(a)
    }
    

    运行结果

    面积来自graph1, 它的面积是39.69
    面积来自graph2, 它的面积是36.316811075498
    所有图形的总面积是:76.006811075498
    

    如果再增加长方形的面积,不需要对totalArea()做任何更改。

    首先定义Rectangle结构体和source()calculate()方法

    // Rectangle 结构体, 长方形
    type Rectangle struct {
        graphName string
        length    float64
        width     float64
    }
    
    // source Rectangle的方法
    func (r Rectangle) source() string {
        return r.graphName
    }
    
    // calculate Rectangle的方法
    func (r Rectangle) calculate() float64 {
        return r.length * r.width
    }
    

    修改主函数

    func main() {
        graph1 := Square{
            graphName:  "graph1",
            sideLength: 6.3,
        }
        graph2 := Round{
            graphName: "graph2",
            radius:    3.4,
        }
        graph3 := Rectangle{
            graphName: "graph3",
            length:    12.3,
            width:     6.5,
        }
        a := []Area{graph1, graph2, graph3}
        totalArea(a)
    }
    

    运行结果

    面积来自graph1, 它的面积是39.69
    面积来自graph2, 它的面积是36.316811075498
    面积来自graph3, 它的面积是79.95
    所有图形的总面积是:155.95681107549802
    

    相关文章

      网友评论

          本文标题:【go语言学习】面向对象oop

          本文链接:https://www.haomeiwen.com/subject/rjbcuktx.html