前置常识

在开始之前,咱们先来了解一点需求先知道的小常识:

  1. 首先,Golang 本身不是一个面向目标编程的言语,所以Go中没有class的概念,可是它是支撑类型的,因而咱们能够运用 struct结构体目标来模仿class类目标。
  2. 什么是面向目标? 引证维基百科上的,能够知道,面向目标是一种目标概念的笼统编程思维。

维基百科:面向目标程序规划(英语:Object-oriented programming,缩写:OOP)是种具有目标概念的编程典范,同时也是一种程序开发的笼统政策。它或许包括数据、特性、代码与办法。目标则指的是类(class)的实例。

  1. 面向目标的三大特征:封装,承继,多态。

了解这些后,咱们开始吧!

怎么封装

意图:躲藏目标特点和完成细节,对外露出揭露接口,增强安全,简化编程意图:躲藏目标特点和完成细节,对外露出揭露接口,增强安全,简化编程

这儿提到了躲藏,在 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 大厂
}

这样就完成了特点的承继,最终,能够看到,两条打印信息是一样的,咱们打印的字面量不同,为什么会相同成果呢?那因为以下几个原理:

  1. 先判断UserName是否归于ITPerson,如果有就拜访
  2. 如果没有,继续找它承继的结构体Person,如果有就拜访,依此类推,直到没有报错
  3. 如果一个结构中承继了多个结构体,而这些结构体之间有相同字段,那么就必须运用完整引证拜访

当咱们知道会这样查找之后,这好像又引出一个点子,那是不是能够截断引证,就像重载那样,既然想到这了,那就完成一下吧。

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到达承继的效果