面向对象基础-接口
我们继续golang面向对象设计的必要知识:接口
说明
在go语言里,接口是一种类型,用type描述,指定类型为interface,里面只包含该接口的方法,不包含变量。
type UserReader interface {
Read() string
Write(string) string
ShowMe()
}
这里对比下我们之前学习的结构体类型和接口类型。(因为接口也是一种类型,同type struct一样,所以type interface在应用时也存在需要实例化或者赋值给一个明确的变量的步骤,后面会看到例子。)
type xxx struct{
....
}
type yyy interface{
...
}
上面一个是定义了叫xxx的结构体,一个是定义了叫yyy 的接口,是两种类型。
我们在mylib包的user.go文件里继续工作…
package mylib
import (
"fmt"
)
type MyUser struct {
Id int
Name string
UserGroup string
CashBanlance float32
OtherBanlance float32
IfDel bool
ModifyTime string
}
type UserControl interface {
Read() string
Write(string) string
}
//实现MyUser类的方法
func (e MyUser) ShowMe() {
fmt.Println("id=", e.Id)
fmt.Println("name=", e.Name)
fmt.Println("group=", e.UserGroup)
fmt.Println("cash=", e.CashBanlance)
fmt.Println("other=", e.OtherBanlance)
fmt.Println("time=", e.ModifyTime)
}
//用MyUser类去实现我们设计的接口,使MyUser类能够提供该接口
func (e MyUser) Read() string {
return "my name is " + e.Name
}
func (e MyUser) Write(str string) string {
e.Name = str
return "my name now is" + e.Name
}
在上面,我们继续使用了myuser类(他包含一个结构体,一个showme方法),同时我们增加一个用户读写接口。并在myuser类上实现该接口。也就是最后两个函数,
func (e Myuser) Read() string{…}
func (e MyUser) Write(str string) string{…}
这里需要注意的是,UserControl接口定义了两个方法,要实现该接口,就必须完整实现他包含的所有方法,这里就是需要你实现两个方法,一个write一个read。只要实现了接口定义的所有方法,你就实现了该接口。另外一点,一个接口可以不包含方法,这个接口就是空接口,即interface{}
接口应用
接口定义好了,如何使用?
最简单的用法,跟类一样,实例化之后即可使用,本例即为
package main
import (
"fmt"
"mylib"
)
func main() {
user1 := mylib.MyUser{
1,
"wen",
"A2",
15.93,
16.88,
true,
"2020-05-01 14:55:32",
}
//定义一个接口
var userc mylib.UserControl
//接口赋值
userc = user1
fmt.Println(userc.Read())
}
执行结果如下
因为user1对应的类MyUser已经实现了接口UserControl所要求的所有方法,一个write,一个read,所以MyUser类的实例可以直接赋值给接口UserControl的实例。也就是这里的userc=user1,然后就可以通过userc调用接口里规定的方法了。
(这种接口实现方式,叫做隐式接口,是GO语言特点之一,有别于java这样的传统面向对象语言,java中接口需要显式的声明。)
我们再来看看该接口的另外一个方法,write。测试代码如下:
package main
import (
"fmt"
"mylib"
)
func main() {
user1 := mylib.MyUser{
1,
"wen",
"A2",
15.93,
16.88,
true,
"2020-05-01 14:55:32",
}
var userc mylib.UserControl
userc = user1
fmt.Println(userc.Read())
//上面是两部并一步,先执行userc.Read()方法,再调用fmt的print模块打印
//该方法return的值。
//调用该接口的方法,变更新名字
userc.Write("newname")
fmt.Println(userc.Read())
}
输出的结果是:
跟设想的不一样?本意是通过接口提供的方法变更数据对应的值,但仍然输出原名称。
这里涉及到GO语言参数传值方式。简单来讲,go语言参数传递,默认是值拷贝的方式,也就是在实现接口时
func (e Myuser) Read() string{…}
func (e MyUser) Write(str string) string{…}
两个方法,都复制了一份实际传参时的拷贝数据在方法内部(局部空间内)无法影响到参数对应数据本身。
因此我们用指针方式来实现接口构造,目的是为了接口接受实例化或者初始化后,接口的方法是直接影响给予这个接口赋值的原始对象的。
func (e *Myuser) Read() string{…}
func (e *MyUser) Write(str string) string{…}
用 * 号表示,采用对象的指针来传值,也就是方法直接作用到初始数据对象。
在接口初始化时,使用userc = &user1 形式,将struct user1 的指针赋值给userc,
这样userc和user1实际指向了内存中同一个数据对象块。
调整后的代码和效果如下:
user.go
package mylib
import (
"fmt"
)
type MyUser struct {
Id int
Name string
UserGroup string
CashBanlance float32
OtherBanlance float32
IfDel bool
ModifyTime string
}
type UserControl interface {
Read() string
Write(string) string
ShowMe()
}
func (e MyUser) ShowMe() {
fmt.Println("id=", e.Id)
fmt.Println("name=", e.Name)
fmt.Println("group=", e.UserGroup)
fmt.Println("cash=", e.CashBanlance)
fmt.Println("other=", e.OtherBanlance)
fmt.Println("time=", e.ModifyTime)
}
func (e *MyUser) Read() string {
return "my name is " + e.Name
}
func (e *MyUser) Write(str string) string {
e.Name = str
return "my name now is " + e.Name
}
mymain.go
package main
import (
"fmt"
"mylib"
)
func main() {
user1 := mylib.MyUser{
1,
"wen",
"A2",
15.93,
16.88,
true,
"2020-05-01 14:55:32",
}
var userc mylib.UserControl
userc = &user1
fmt.Println(userc.Read())
fmt.Println(userc.Write("newname"))
userc.Read()
user1.ShowMe()
}
输出结果:
可以看到,userc.Read()和user1.ShowMe()都显示出,user1内的Name发生了实际的变化。
尾声
这部分后面的内容,如果之前学习使用过指针有所了解,会好一点。go设计者有以前设计C语言编译器的,所以沿用了一部分C的设计思想。
吾听风雨
吾览江山
常觉风雨江山外
有万不得已者在
…