欢迎光临散文网 会员登陆 & 注册

Go语言面向对象

2022-10-23 23:53 作者:苦茶今天断更了吗  | 我要投稿

Go语言面向对象

①Golang也支持面向对象编程(OOP)。

②Golang没有类(class),Go语言的结构体struct和其它编程语言的类class有同等的地位,可以理解成Golang是基于struct来实现OOP特性的。

③Golang去掉了传统OOP语言的继承、方法重载、构造函数析构函数、隐藏的this指针等。

④Golang有OOP的继承,封装多态的特性,只是实现的方式不一样。

⑤OOP是语言类型系统(type system)的一部分,通过接口(interface)关联,耦合性低,也非常灵活。在Golang 中面向接口编程是非常重要的特性。

 

声明:

type结构体名称 struct

{field1 type

field2 type

}

 

字段/属性:

①从概念上看:结构体字段 = 属性 = field (即授课中,统一叫字段)

②字段是结构体的一个组成部分,一般是基本数据类型、数组,也可是引用类型。

③创建结构体变量后,若未赋值,则为默认值:

布尔类型是 false,数值是0,字符串是""。score [3]int 则为[0,0,0]

指针,slice, map的零值都是nil,即还没有分配空间

④不同结构体变量的字段是独立,互不影响,一个结构体变量字段的更改,不影响另外一个,结构体是值类型。


 

 

创建结构体变量访问结构体字段

①var person Person

②var person Person = Person{}:“p2 := Person{ "mary",20}”

③var person *Person = new (Person)

(*p3).Name = "smith"

p3.Name = "john"

④var person *Person =&Person{}

A.var person *Person =&Person{ "mary",20}

B.(*person).Name = "scott"

person. Name = "scott~~"

③④返回的是结构体指针。

Go支持结构体指针.字段名。go编译器底层对person.Name做了转化(*person).Name

 

struct类型的内存分配机制

①结构体的所有字段在内存中是连续的。

②结构体是用户单独定义的类型,和其它类型进行转换时,需要有完全相同的字段(名字、个数、类型)

③结构体进行type重新定义(相当于取别名),Golang认为是新的数据类型,但是相互间可以强转。stu2 =Stu(stu1)

④ struct的每个字段上,可以写上一个tag。该tag可以通过反射机制获取,常见的使用场景就是序列化反序列化。


方法:

 


①给Person结构体添加speak方法,输出xxx是一个好人。

②给Person结构体添加jisuan方法,可接收一个数n,计算从1+..+n的结果,说明方法体内可进行运算。

方法的调用和传参机制原理:重要!

①变量调用方法时,该变量本身也会作为一个参数传递到方法(如果变量是值类型,则进行值拷贝,如果变量是引用类型,则进行地质拷贝)

②结构体类型是值类型,在方法调用中,遵守值类型的传递机制,是值拷贝传递方式。

③如程序员希望在方法中,修改结构体变量的值,可以通过结构体指针的方式来处理。

④Golang中的方法是作用在指定的数据类型上的(即:和指定的数据类型绑定),因此自定义类型,都可以有方法,而不仅仅是struct。

⑤方法的访问范围控制的规则。方法名首字母小写,只能在本包访问,方法首字母大写,可以在本包和其它包访问。

⑥如果一个类型实现了String()这个方法,那么fmt.Println默认会调用这个变量的String()进行输出。

 

方法和函数的区别:

①调用方式不同:

函数:函数名(实参列表)
方法:变量.方法名(实参列表)

②对于普通函数,接收者为值类型时,不能将指针类型的数据直接传递,反之亦然。

③对于方法,接收者为值类型时,可以直接用指针类型的变量调用方法,反过来同样也可以。

 


不管调用形式如何,真正决定是值拷贝还是地址拷贝,看这个方法是和哪个类型绑定

若是和值类型,比如(p Person),则是值拷贝;

若和指针类型,比如(p *Person),则是地址拷贝。

 

案例:

①编写一个Student结构体,包含name、gender、age、id、score字段,分别为 string、string、int、int、float64类型。

②结构体中声明一个say方法,返回string类型,方法返回信息中包含所有字段值。

③在main方法中,创建Student结构体实例(变量),并访问 say方法,将结果打印输出。

 


 创建结构体变量时指定字段值


Golang的结构体没有构造函数,通常可以使用工厂模式来解决这个问题。

使用工厂模式实现跨包创建结构体实例(变量)的案例:

如果model包的结构体变量首字母写,引入后,直接使用,没有问题。

如果model包的结构体变量首字母写,引入后,不能直接使用,可以工厂模式解决。

 

抽象:

我们在前面去定义一个结构体时候,实际上就是把一类事物的共有的属性(字段)和行为(方法)提取出来,形成一个物理模型(结构体)。这种研究问题的方法称为抽象。

 

封装:

封装(encapsulation)就是把抽象出的字段和对字段的操作封装在一起,数据被保护在内部,程序的其它包只有通过被授权的操作(方法),才能对字段进行操作。

 

①将结构体、字段(属性)的首字母小写(不能导出了,其它包不能使用,类似private)

②给结构体所在包提供一个工厂模式的函数,首字母大写。类似一个构造函数

③提供一个首字母大写的Set方法(类似其它语言的public),用于对属性判断并赋值

func (var结构体类型名) Get.Xxx(参数列表)(返回值列表){

//加入数据验证的业务逻辑

var.字段 = 参数

}

④提供一个首字母大写的Get方法(类似其它语言的 public),用于获取属性的值

func (var结构体类型名)GetXxx() {

return var.age;

}

 

继承:

嵌套匿名结构体的基本语法

type Goods struct

{Name string

Price int

}

type Book stiuct {

Goods //嵌套匿名结构体Goods

Writer string

}

结构体可以使用嵌套匿名结构体所有的字段和方法,即:首字母大写或者小写的字段、方法,都可以使用

 

②匿名结构体字段访问可以简化。


 

当我们直接通过 b访问字段或方法时,其执行流程如下比如 b.Name:

编译器会先看b对应的类型有没有Name,如果有,则直接调用B类型的Name字段

如果没有,就去看B中嵌入的匿名结构体A有没有声明Name字段,如果有就调用,如果没有继续查找….如果都找不到就报错。

 

③当结构体匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望访问匿名结构体的字段和方法,可以通过匿名结构体名来区分。

 

④结构体嵌入两个(或多个)匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指定匿名结构体名字,否则编译报错。

 


⑤如果一个struct嵌套了一个有名结构体,这种模式就是组合,如果是组合关系,那么在访问组合的结构体的字段或方法时,必须带上结构体的名字。

 


⑥嵌套匿名结构体后,也可以在创建结构体变量(实例)时,直接指定各个匿名结构体字段的值。

 如果一个结构体有int类型的匿名字段,就不能有第二个。

如果需要有多个int的字段,则必须给int字段指定名字。

 


如一个struct 嵌套了多个匿名结构体,那么该结构体可以直接访问嵌套的匿名结构体的字段和方法,从而实现了多重继承。

如嵌入的匿名结构体有相同的字段名或者方法名,则在访问时,需要通过匿名结构体类型名来区分。

 

接口:

interface类型可以定义一组方法,但是这些不需要实现。并且 interface不能包含任何变量。到某个自定义类型(比如结构体 Phone)要使用的时候,在根据具体情况把这些方法写出来(实现)

①接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态高内聚低偶合的思想。

②Golang中的接口,不需要显式的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现这个接口。因此,Golang中没有implement这样的关键字。

 

③接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量(实例)。

④一个自定义类型只有实现了某个接口,才能将该自定义类型的实例(变量)赋给接口类型。

⑤只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型。

 


⑥一个自定义类型可以实现多个接口。

 


⑦Golang接口中不能有任何变量。

⑧interface类型默认是一个指针(引用类型),如果没有对interface初始化就使用,那么会输出nil。

⑨一个接口(比如A接口)可以继承多个别的接口(比如B,C接口),这时如果要实现A接口,也必须将B,C接口的方法也全部实现。

 


⑩空接口interface{}没有任何方法,所以所有类型都实现了空接口,即我们可以把任何一个变量赋给空接口

 


 

接口编程:

实现对Hero结构体切片的排序:sort.Sort(data Interface)

①当A结构体继承了B结构体,那么A结构就自动的继承了B结构体的字段和方法,并且可以直接使用。

②当A结构体需要扩展功能,同时不希望去破坏继承关系,则可以去实现某个接口即可,因此我们可以认为:实现接口是对继承机制的补充

 

接口和继承解决的解决的问题不同

继承:解决代码的复用性和可维护性。

接口:设计好各种规范(方法),让其它自定义类型去实现这些方法。

接口比继承更加灵活,继承是满足is - a的关系,而接口只需满足like- a的关系。

接口在一定程度上实现代码解耦。


多态:

在Go语言,多态特征是通过接口实现的。按照统一的接口来调用不同的实现。这时接口变量就呈现不同的形态。

 

接口体现多态的两种形式:

多态参数:上图usb接口变量就体现出多态的特点。

多态数组:如,给Usb数组中,存放Phone 结构体和Camera结构体变量。

 

类型断言

由于接口是一般类型,不知道具体类型,如果要转成具体类型,就需要使用类型断言。

在进行类型断言时,如果类型不匹配,就会报panic,因此进行类型断言时,要确保原来的空接口指向的就是断言的类型。

如何在进行断言时,带上检测机制,如果成功就ok,否则也不要报panic。

 

类型断言的最佳实践:编写一个函数,可以判断输入的参数是什么类型

 


Go语言面向对象的评论 (共 条)

分享到微博请遵守国家法律