将面向对象思想渗入Go中——Go
前置知识
在开始之前,我们先来了解一点需要先知道的小知识:
- 首先,
Golang
本身不是一个面向对象编程的语言,所以Go
中没有class
的概念,但是它是支持类型的,因此我们可以使用struct
结构体对象来模拟class
类对象。 - 什么是面向对象? 引用维基百科上的,可以知道,面向对象是一种对象概念的抽象编程思想。
维基百科:面向对象程序设计(英语:Object-oriented programming,缩写:OOP)是种具有对象概念的编程典范,同时也是一种程序开发的抽象方针。它可能包含数据、特性、代码与方法。对象则指的是类(class)的实例。
- 面向对象的三大特征:封装,继承,多态。
了解这些后,我们开始吧!
如何封装
目的:隐藏对象属性和实现细节,对外暴露公开接口,增强安全,简化编程
这里提到了隐藏,在 Go
中是没有 public、private、protected
这些关键字的,它是通过 首字母大小写控制是否为私有
我们创建一个 model/userInfo.go
文件,在文件中定义一个 userinfo
的结构体
// model/userInfo.go
// 首字母大小写,控制是否私有
type userInfo struct {
Username string
Age int
Gender string
Height float32
Education string
MoreInfo map[string]interface{}
}
此时,我们在外部的 main.go
中是无法拿到这个 userInfo
的,这时候,我们要的隐藏
特点,就实现了,但是只是隐藏了,但是我们需要使用它啊,所以我们想在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。 这时候就要用到设计模式中的工厂模式
了。
代码实现:
// 工厂模式:生成对象,隐藏逻辑
func NewUserInfo(
Username string,
Age int,
Gender string,
Height float32,
Education string,
MoreInfo map[string]interface{},
) *userInfo {
return &userInfo{
Username,
Age,
Gender,
Height,
Education,
MoreInfo,
}
}
这时候,我们就实现了对 userInfo
这个私有 [类] 的封装了,我们就可以在外部main.go
中愉快的使用了。
// main.go
package main
import (
"fmt"
"object_oriented_demo/model"
)
func main() {
// 无法访问到
// p1 := &model.UserInfo{
// Username: "陪我去看海吧",
// Age: 22,
// Gender: "男",
// Height: 178,
// Education: "本科",
// MoreInfo: nil,
// }
p1 := model.NewUserInfo("陪我去看海吧", 22, "男", 178, "本科", nil)
fmt.Println(p1) // &{陪我去看海吧 22 男 178 本科 map[]}
}
如何继承
目的:子类继承父类,自动拥有父类的属性和方法,提供代码复用率,扩展性与维护性
可以知道,继承是两个或多个类的作用,子类拥有父类的一切
在 model/userInfo.go
中创建两个 struct
结构体(Person, ITPerson
)将父[类]写入子[类]属性即可
// model/userInfo.go
// 父
type Person struct {
UserName string
Age int
Gender string
}
// 子
type ITPerson struct{
Person
Company string
}
这样 ITPerson
就继承了 Person
所有的属性了。
// main.go
package main
import (
"fmt"
"object_oriented_demo/model"
)
func main() {
itPerson := &model.ITPerson{
Person: model.Person{
UserName: "陪我去看海吧",
Age: 22,
Gender: "男",
},
Company: "大厂",
}
fmt.Println(itPerson.Person.UserName, itPerson.Person.Age, itPerson.Company) // 陪我去看海吧 22 大厂
fmt.Println(itPerson.UserName, itPerson.Age, itPerson.Company) // 陪我去看海吧 22 大厂
}
这样就实现了属性的继承,最后,可以看到,两条打印信息是一样的,我们打印的字面量不同,为什么会同样结果呢?那因为以下几个原理:
- 先判断UserName是否属于ITPerson,如果有就访问
- 如果没有,继续找它继承的结构体Person,如果有就访问,依此类推,直到没有报错
- 如果一个结构中继承了多个结构体,而这些结构体之间有相同字段,那么就必须使用完整引用访问
当我们知道会这样查找之后,这似乎又引出一个点子,那是不是可以截断引用,就像重载
那样,既然想到这了,那就实现一下吧。
在model/userInfo.go
中添加两个方法
// model/userInfo.go
// Person 的Info方法
func (_this *Person) Info() {
fmt.Printf("Info = %v \n", _this)
}
// 重写
func (_this *ITPerson) Info() {
fmt.Printf("ITPerson Info = %v \n", _this)
}
main.go
中使用:
// main.go
itPerson.Info() // ITPerson Info = &{{陪我去看海吧 22 男} {pkc} 大厂}
p3 := model.Person{
UserName: "PKC",
Age: 22,
Gender: "男",
}
p3.Info() // Info = &{PKC 22 男}
既然重载
都模拟了,那继承方法还不简单吗,只需要不重载就可以使用到继承的Info
方法了呀!
如何多态
特征:父类引用指向子类指针,一话说就是:允许将子类类型的指针赋值给父类类型的指针。
在go
中我们只有使用interface
来实现这一特征,我们定义一个 struct
和 一个interface
// main.go
type myObject interface{
getValue()
setValue(newValue string)
}
type object_class struct{
value string
}
func (_this *object_class) getValue() {
fmt.Println("Value: ", _this.value)
}
func (_this *object_class) setValue(newValue string) {
_this.value = newValue
fmt.Println("set newValue: ", newValue)
}
两个不同对象调用方法
// main.go
func main() {
object_instance := &object_class{value: "陪你去看海"}
object_instance.getValue()
object_instance.setValue("陪你去看海吧!")
var _myObject myObject
_myObject = object_instance
_myObject.getValue()
_myObject.setValue("陪我去看海吧")
/*
Value: 陪你去看海
set newValue: 陪你去看海吧!
Value: 陪你去看海吧!
set newValue: 陪我去看海吧
*/
}
在上述代码体现出了一个点:一个变量实现了接口当中的所有方法,那么这个接口就可以指向这个对象
,和特征是不是很匹配呢!
总结
- 面向对象三大特征:封装,继承,多态。
Go
是无class
语言,使用struct
对属性进行封装Go
没有public, private
这些关键字,使用首字母大小写辨别是否私有- 在
struct
中嵌套struct
达到继承的效果
转载自:https://juejin.cn/post/7199250631114227771