likes
comments
collection
share

将面向对象思想渗入Go中——Go

作者站长头像
站长
· 阅读数 56

前置知识

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

  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达到继承的效果
转载自:https://juejin.cn/post/7199250631114227771
评论
请登录