众所周知Golang并不是一个面向对象的语言,但他却可以用别的方式去实现.
一个例子,假设一个打卡系统,有入门和出门的操作,我们首先定义Student和Teacher两个对象(在Golang中为结构体)(并且有一个ClassInfo的类嵌套).他们拥有学号,名字,班级信息三个属性.
type ClassInfo struct {
Class int
No int
}
type Student struct {
Class ClassInfo
Name string
id int
}
type Teacher struct {
Class ClassInfo
Name string
id int
}
然后给他们分别写各自的入门和出门的方法.
func (stu Student) inRoom() {
fmt.Println(stu.Name, "student in room")
ch <- stu.Name
}
func (stu Student) outRoom() {
fmt.Println(stu.Name, "student out room")
ch <- stu.Name
}
func (tea Teacher) inRoom() {
fmt.Println(tea.Name, "teacher in room")
ch <- tea.Name
}
func (tea Teacher) outRoom() {
fmt.Println(tea.Name, "teacher out room")
ch <- tea.Name
}
这样的话我们回到main函数中,我们声明两个对象,只能这样一个一个调用他们的方法.
func main() {
var stu Student = Student{
Class: ClassInfo{Class: 1, No: 1},
Name: "John",
id: 223344,
}
var tea Teacher = Teacher{
Class: ClassInfo{Class: 2, No: 0},
Name: "miku",
id: 000000,
}
stu.inRoom()
stu.outRoom()
tea.inRoom()
tea.outRoom()
....
如果对象的方法多起来,那么一个一个调用是非常愚蠢的.
所以我们可以为他们的方法做一个打包.
func doStudent(stu Student) {
stu.inRoom()
stu.outRoom()
}
func doTeacher(tea Teacher) {
tea.inRoom()
tea.outRoom()
}
这样只用调用这一个doStudent或者doTeacher就好了,但这样并不够好,如果类型也变得更多了该怎么办?来了个书记struct,校长struct....
所以我们必须定义一个接口,就像下面这样.
type Person interface {
inRoom()
outRoom()
}
这个Person接口很简单,他只用实现入门和出门两个操作就行. 然后像上面一样对Person所做的事情做一个打包方便调用
func PersonCheckAll(p Person) {
p.inRoom()
p.outRoom()
}
那么主函数就变成这样写, 我们定义一个Person实例, 当他需要是什么类型时,他就变成什么类型, 有点像OOP的 Person p = new Student() 不是么,实现多态,这就是这里接口的作用.
var personImplement Person
personImplement = stu
personImplement.inRoom()
personImplement.outRoom()
PersonCheckAll(personImplement)
其实这个例子并不恰当,因为入门和出门并不是什么需要并发的东西,为了显示Golang优雅的并发,我们考虑Teacher和Student一起,有大量的人一起入门的情况.
我们只需要声明一个Person空接口,这个空接口其实有点泛型的意思,一个能容纳下不同类型的list. 我们把tea和stu都扔进去. 并且遍历入门打卡.
p := [...]Person{stu, tea}
for _, v := range p {
go v.inRoom()
}
for i := 0; i < len(p); i++ {
msg := <-ch // 等待信道返回消息。
fmt.Println("finish", msg)
}
go关键字能够轻松开启一个协程,我们使不同对象的入门操作变为并发了.
并且这里回应两上面inRoom,outRoom函数中的ch <- tea.Name是什么意思,这是将此协程的信息通过channel进行传输.
完整的代码:
package main
import (
"fmt"
)
type Person interface {
inRoom()
outRoom()
}
type ClassInfo struct {
Class int
No int
}
type Student struct {
Class ClassInfo
Name string
id int
}
type Teacher struct {
Class ClassInfo
Name string
id int
}
func (stu Student) inRoom() {
fmt.Println(stu.Name, "student in room")
ch <- stu.Name
}
func (stu Student) outRoom() {
fmt.Println(stu.Name, "student out room")
ch <- stu.Name
}
// func doStudent(stu Student) {
// stu.inRoom()
// stu.outRoom()
// }
func (tea Teacher) inRoom() {
fmt.Println(tea.Name, "teacher in room")
ch <- tea.Name
}
func (tea Teacher) outRoom() {
fmt.Println(tea.Name, "teacher out room")
ch <- tea.Name
}
// func doTeacher(tea Teacher) {
// tea.inRoom()
// tea.outRoom()
// }
func PersonCheckAll(p Person) {
p.inRoom()
p.outRoom()
}
var ch = make(chan string, 2)
func main() {
var stu Student = Student{
Class: ClassInfo{Class: 1, No: 1},
Name: "John",
id: 223344,
}
var tea Teacher = Teacher{
Class: ClassInfo{Class: 2, No: 0},
Name: "miku",
id: 000000,
}
// var personImplement Person
// personImplement = stu
// personImplement.inRoom()
// personImplement.outRoom()
// PersonCheckAll(personImplement)
// personImplement = tea
// personImplement.inRoom()
p := [...]Person{stu, tea}
for _, v := range p {
go v.inRoom()
}
for i := 0; i < len(p); i++ {
msg := <-ch // 等待信道返回消息。
fmt.Println("finish", msg)
}
fmt.Println("Done!")
}