四时宝库

程序员的知识宝库

Golang之方法使用(golang怎么使用)

写在前面:

Golang中方法是面向对象的主体,Golang中没有像其他语言那么明确定定义OOP,而是对一个对象和一个函数进行关联,通过特殊的标记来分辨一个对象的方法,从理解的角度来看,可以说一个接口的定义实现在了一个对象上面,其中接口和对象的耦合很低。

0x01 -- 方法

在Golang中,方法的使用频率相当高,是重点需要掌握的组件功能,方法类似于对一个对象行为的实现,比如一只猫可以跑,可以跳,猫是主体,跑是猫的行为,一个函数,在其名字之前放上一个变量,即是一个方法。

Bash
package main

import (
   "fmt"
   "testing"
)

// 定义结构
type Cat struct {
   name string
   age int
}
// 实现跑的方法
func(p Cat) Run() {
   fmt.Println(p.name + "在跑")
}
// 实现跳的方法
func(p Cat) Jump() {
   fmt.Println(p.name + "在跳")
}

// 给小猫年龄加一岁
func(p Cat) addAge() {
   p.age = p.age + 1
   fmt.Printf("%s的年龄增加1岁:%d \n", p.name, p.age)
}
// 给小猫年龄加a岁
func(p *Cat) addAgePtr(a int) {
   p.age = p.age + a
   fmt.Printf("%s的年龄增加%d岁:%d \n", p.name, a, p.age)
}
func TestMethod(t *testing.T) {
   cat1 := Cat{"小花", 2}
   fmt.Printf("猫名: %s ,年龄:%d \n", cat1.name,cat1.age)
   cat1.Run()
   cat1.Jump()

   cat1.addAge() // 给小花猫增加一岁
   // 我们在这里在打印一下小花猫的信息
   fmt.Printf("猫名: %s ,年龄:%d \n", cat1.name,cat1.age)

   cat1.addAgePtr(1) // 给小花猫增加一岁
   // 我们在这里在打印一下小花猫的信息
   fmt.Printf("猫名: %s ,年龄:%d \n", cat1.name,cat1.age)
}

输出:

Bash
=== RUN   TestMethod
猫名: 小花 ,年龄:2 
小花在跑
小花在跳
小花的年龄增加一岁:3 
猫名: 小花 ,年龄:2 
小花的年龄增加1岁:3 
猫名: 小花 ,年龄:3 
--- PASS: TestMethod (0.00s)
PASS

以上代码实现了简单的方法实现,方法就像是对主体的一种验证,成员就是主体的静态属性,方法是主体的动态属性,如果学过Java会对此比较熟悉。

方法的定义就是在函数名和func之间加上对象的主体,一是声明当前方法是属于哪个结构体,二是给方法提供一个结构体的调用对象,在方法中使用结构体的成员信息可以通过链式调用进行调取。

上面的示例中需要注意下,在定义方法时有两种结构体类型,一种是值类型,一种是指针类型,看代码示例的输出可以分析下不同,这里我直接说明,在使用值类型结构体作为参数时,在方法中对结构体成员进行操作,影响范围仅限于当前方法中,比如我们在执行addAge方法时,在方法内对age进行了+1,但是我们在执行完此方法后进行打印结构体的age时,发现age还是2,没有变化。

在执行方法addAgePtr时,我们定义的主体类型是指针型,方法中对age进行+a,在完成方法调用后,我们对结构体进行打印,可以看到age已经变化了,这是因为在使用指针作为类型传递时,会传递当前结构体的内存地址指针,在方法中做的修改,是对结构体本身进行的修改,而在使用值类型时,传递给方法的其实是结构体的复制。

在开发中需要注意区别,大部分情况我们会使用指针作为结构体参数传递给方法。


0x02 -- 接口(interface)

接口是一种类型,它只定义了声明,没有具体实现,接口的作用是可以使你的代码从具体的实现中去耦,接口中可以定义很多声明,至于由哪个实体去实现,接口不关心,接口可以对某一类描述行为进行聚合,其定义是:

接口是一组仅包含方法名、参数、返回值的未具体实现的方法的集合

Bash
package main

import (
   "fmt"
   "testing"
)

type animal interface {
   Run()
   Jump()
   eatMeat()
}

type Dog struct {
   name string
   age int
   eat string
}

// 实现跑的方法
func(d Dog) Run() {
   fmt.Println(d.name + "在跑")
}

// 实现跳的方法
func(d Dog) Jump() {
   fmt.Println(d.name + "在跳")
}

func(d Dog) eatMeat(){

   fmt.Println(d.name + "在吃肉")
}

func(d Dog) addAge(){
   d.age = d.age + 1
}


// 定义结构
type Cat struct {
   name string
   age int
}
// 实现跑的方法
func(c Cat) Run() {
   fmt.Println(c.name + "在跑")
}
// 实现跳的方法
func(c Cat) Jump() {
   fmt.Println(c.name + "在跳")
}

// 给小猫年龄加一岁
func(c Cat) addAge() {
   c.age = c.age + 1
   fmt.Printf("%s的年龄增加1岁:%d \n", c.name, c.age)
}
// 给小猫年龄加a岁
func(p *Cat) addAgePtr(a int) {
   p.age = p.age + a
   fmt.Printf("%s的年龄增加%d岁:%d \n", p.name, a, p.age)
}

接口在使用中,接口后结构体没有强关联性,结构体可以实现一个接口的部分方法,也可以实现未在接口中定义的方法,同时多个结构体可以实现同一个方法,如果有结构体实现了接口的所有方法,则认为实现了该接口,在开发工具中会对接口实现的结构体进行关联:

如果实现了所有的接口,则可以通过工具方便查看接口所在位置,并追溯。


0x03 -- new

在Golang中没有构造函数,可以自己实现一个构造函数,构造函数的目的在于初始化一个结构体,虽然没有构造函数,但是Golang中有new关键字,可以通过new关键字实现实例化一个结构体:

Bash
dog1 := new(Dog)
dog2 := &Dog{}
dog3 := Dog{}

dog1和dog2是等效的,实例化一个结构体并返回结构体的指针,dog3会实例化一个结构体,返回结构体本身。


0x03 -- 总结

本篇学习了如何定义一个结构体的方法,如何对多个结构体相同的方法进行抽象成接口,创建方法时值传递还是指针传递做了对比,希望各位学者可以多多实践,起码文章中的示例要自己手敲一遍,多实践才能了解Golang开发者的意图,体会到Go语言奥妙。

发表评论:

控制面板
您好,欢迎到访网站!
  查看权限
网站分类
最新留言
    友情链接