前置常识
在开始之前,咱们先来了解一点需求先知道的小常识:
- 首先,
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
到达承继的效果