什么是面向对象

在了解 Go 语言是不是面向对象(简称:OOP) 之前,我们必须先知道 OOP 是啥,得先给他 “下定义”。

根据 Wikipedia 的定义,我们梳理出 OOP 的几个基本认知:

  • 面向对象编程(OOP)是一种基于 "对象" 概念的编程范式,它可以包含数据和代码:数据以字段的形式存在(通常称为属性或属性),代码以程序的形式存在(通常称为方法)。

  • 对象自己的程序可以访问并经常修改自己的数据字段。

  • 对象经常被定义为类的一个实例。

  • 对象利用属性和方法的私有/受保护/公共可见性,对象的内部状态受到保护,不受外界影响(被封装)。

基于这几个基本认知进行一步延伸出,面向对象的三大基本特性:

  • 封装。

  • 继承。

  • 多态。

至此对面向对象的基本概念讲解结束,想更进一步了解的可自行网上冲浪。

Go 是面向对象的语言吗

“Go 语言是否一门面向对象的语言?”,这是一个日经话题。官方 FAQ 给出的答复是:

是的,也不是。原因是:

  • Go 有类型和方法,并且允许面向对象的编程风格,但没有类型层次。

  • Go 中的 "接口 "概念提供了一种不同的方法,我们认为这种方法易于使用,而且在某些方面更加通用。还有一些方法可以将类型嵌入到其他类型中,以提供类似的东西,但不等同于子类。

  • Go 中的方法比 C++ 或 Java 中的方法更通用:它们可以为任何类型的数据定义,甚至是内置类型,如普通的、"未装箱的 "整数。它们并不局限于结构(类)。

  • Go 由于缺乏类型层次,Go 中的 "对象 "比 C++ 或 Java 等语言更轻巧。

Go 实现面向对象编程

封装

面向对象中的 “封装” 指的是可以隐藏对象的内部属性和实现细节,仅对外提供公开接口调用,这样子用户就不需要关注你内部是怎么实现的。

在 Go 语言中的属性访问权限,通过首字母大小写来控制:

  • 首字母大写,代表是公共的、可被外部访问的。无论是方法名、常量、变量名还是结构体的名称

  • 首字母小写,代表是私有的,不可以被外部访问。

Go 语言的例子如下:

package main

import "fmt"

type Animal struct {
	name string
	Age  int
}

func NewAnimal() *Animal {
	return &Animal{}
}

func (p *Animal) SetName(name string) {
	p.name = name
}

func (p *Animal) GetName() string {
	return p.name
}

func main() {
	p := NewAnimal()
	p.SetName("名称")
	fmt.Println(p.GetName())
}
Animalname

继承

面向对象中的 “继承” 指的是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。

从实际的例子来看,就是动物是一个大父类,下面又能细分为 “食草动物”、“食肉动物”,这两者会包含 “动物” 这个父类的基本定义。

extends
type Cat struct {
 Animal
 FeatureA string
}

type Dog struct {
 Animal
 FeatureB string
}
CatDogAnimalCatDogAnimal
package main

import "fmt"

type Animal struct {
	name string
}

func NewAnimal() *Animal {
	return &Animal{}
}

func (p *Animal) SetName(name string) {
	p.name = name
}

func (p *Animal) GetName() string {
	return p.name
}
//-------------------
type Cat struct {
	Animal
	FeatureA string
}

type Dog struct {
	Animal
	FeatureB string
}

func main() {
	p := NewAnimal()
	p.SetName("my name is animal")
	dog := Dog{Animal: *p}
	fmt.Println("继承getName:", dog.GetName())
}
CatDog
func (dog *Dog) HelloWorld() {
	fmt.Println("dog world了")
}

func (cat *Cat) HelloWorld() {
	fmt.Println("cat world了")
}
Animal
package main

import "fmt"

type User struct {
	id   int
	name string
}

type Manager struct {
	User
	title string
}

func (self *User) ToString() string {
	return fmt.Sprintf("User: %p, %v", self, self)
}

func (self *Manager) ToString() string {
	return fmt.Sprintf("Manager: %p, %v", self, self)
}

func main() {
	m := Manager{User{1, "Tom"}, "Administrator"}
	fmt.Println(m.ToString())
	fmt.Println(m.User.ToString())
}

go支持只提供类型而不写字段名的方式,也就是匿名字段,也称为嵌入字段 

多态

面向对象中的 “多态” 指的同一个行为具有多种不同表现形式或形态的能力,具体是指一个类实例(对象)的相同方法在不同情形有不同表现形式。

多态也使得不同内部结构的对象可以共享相同的外部接口,也就是都是一套外部模板,内部实际是什么,只要符合规格就可以。

在 Go 语言中,多态是通过接口来实现的

type AnimalSounder interface {
 MakeDNA()
}

func MakeSomeDNA(animalSounder AnimalSounder) {
 animalSounder.MakeDNA()
}
AnimalSounderMakeSomeDNAAnimalSounder
CatDogMakeSomeDNAAnimalSounder
package main

import "fmt"

type Cat struct {

}

type Dog struct {

}

type AnimalSounder interface {
	MakeDNA()
}

func MakeSomeDNA(animalSounder AnimalSounder) {
	animalSounder.MakeDNA()
}

func (c *Cat) MakeDNA() {
	fmt.Println("Cat DNA")
}

func (c *Dog) MakeDNA() {
	fmt.Println("Dog DNA")
}

func main() {
	MakeSomeDNA(&Cat{})
	MakeSomeDNA(&Dog{})
}
CatDogAnimalSounderMakeSomeDNA

基于类型创建的方法必须定义在同一个包内

type Myint int

func (i Myint) PrintInt ()  {
	fmt.Println(i)
}

func main() {
	var i Myint = 1
	i.PrintInt()
}

总结

通过今天这篇文章,我们基本了解了面向对象的定义和 Go 官方对面向对象这一件事的看法,同时针对面向对象的三大特性:“封装、继承、多态” 在 Go 语言中的实现方法就进行了一一讲解。

在日常工作中,基本了解这些概念就可以了。若是面试,可以针对三大特性:“封装、继承、多态” 和 五大原则 “单一职责原则(SRP)、开放封闭原则(OCP)、里氏替换原则(LSP)、依赖倒置原则(DIP)、接口隔离原则(ISP)” 进行深入理解和说明。

在说明后针对上述提到的概念。再在 Go 语言中讲解其具体的实现和利用到的基本原理,互相结合讲解,就能得到一个不错的效果了。