go-doc-面向对象

go面向对象

  1. Go也支持面向对象编程,但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说go支持面向对象编程。
  2. go没有类,go语言的结构体string和其他编程语言的类有相同的地位
  3. go面向对象编程非常简单,去掉了传统OOP语言的继承、方法重载、构造函数和析构函数、隐藏的this指针等。
  4. go仍然有面向对象编程的继承,封装和多态的特效,只是实现的方式和其他OOP语言不一样
  5. go面向对象很优雅,OOP本身就是语言类型系统的一部分,通过接口interface关联,耦合性低,也非常灵活

结构体

  1. 结构体是自定义数据类型,代表一类事物
  2. 结构体变量时具体的,实际的,代表一个具体变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main 

import (
"fmt"
)

// 定义一个结构体
type Cat struct {
Name string
Age int
Color string
}

func main() {
// 方式1
var cat1 Cat;
cat1.Name = "c1"

fmt.Println(cat1)

// 方式2
var cat2 Cat = Cat { "Name", 20, "Red"}

fmt.Println(cat2)

// 方式3
var cat3 *Cat = new(Cat)
(*cat3).Name = "c3"

// 这种写法也没问题,Go底层做了处理,会转换成(*cat3).Age
cat3.Age = 11

fmt.Println(*cat3)

// 方式4
var cat4 *Cat = &Cat{}
(*cat4).Name = "c4"

fmt.Println(*cat4)
}

结构体要点

  1. 结构体是用户单独定义的类型,和其他类型进行转换时需要完成相同的字段(名字、个数和类型)
  2. 结构体进行type重新定义(相当于取别名),Go任务是新的数据类型,但是相互之间可以强转
  3. struct的每个字段上,可以写上一个tag,该tag可以通过反射机制获取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package main 

import (
"fmt"
"encoding/json"
)

type A struct {
Num int
Name string
}

type B struct {
Num int
Name string
}

type C struct {
Num int `json:"name"`
Name string `json:"age"`
}

type A2 A

func main() {
var a A = A {1, "111"}

var a1 = A {
Num:1,
Name:"111",
}

fmt.Println(a1)

var b B = B {2, "222" }

// 类型转换
var c = A(b)
fmt.Println(c)

// 重名类型,强转
var d = A2(a)
fmt.Println(d)

// tag和序列化
var e C = C {3, "333"}
var jStr, _ = json.Marshal(d)
fmt.Println(string(jStr))

var jStr2, _ = json.Marshal(e)
fmt.Println(string(jStr2))
}

方法

Go中的方法是作用在指定的数据类型上的,因此自定义类型,都可以有方法,而不仅仅是struct

  • 基本语法
1
2
3
4
func (变量名 类型) 方法名 (参数列表) (返回值列表) {
// 操作
return 返回值列表
}

结构体要点

  1. 如果希望在方法中,修改结构体变量的值,可以通过结构体指针的方式处理
  2. go中的方法作用字指定的类型上,因此自定义类型都可以有方法,不仅仅是struct,int等也可以
  3. 方法的访问规则跟函数一样,首字母大小表示公有
  4. 如果一个类型实现了String()方法,那么fmt.Println()会调用这个变量的String()进行输出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package main 

import (
"fmt"
)

type A struct {
Num int
Name string
}

// 方法定义
func (a A) test() {
fmt.Println("test...", a)
}

func (a *A) test2() {
fmt.Println("test2...", *a)
}

// 自定义打印函数
func (a A) String() string {
return "A String"
}

// 在int类型上绑定方法
type interger int
func (i interger) test3() {
fmt.Println("test3...", i)
}

// 定义指针的方法,可以修改成员变量
func (i *interger) test4() {
*i = *i + 1
fmt.Println("test4...", *i)
}

func main() {
var a A = A {1, "111"}
a.test()

fmt.Println(a)

// c可以直接方法带指针的方法,底层处理了
a.test2()
(&a).test2()

// 内置类型加自定义方法
var i interger = 10
i.test3()
i.test4()
}

封装

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

在go开发中没有特别强调封装,这点不想其他语言。

封装的好处

  1. 隐藏实现细节
  2. 可以对数据进行验证,保证安全合理
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main 

import (
"fmt"
)

type A struct {
num int
name string
}

func (a A) GetName() string {
return a.name
}

func main() {
var a A = A {1, "1111"}

fmt.Println(a.GetName())
}

继承

继承可以解决代码复用,让编程更加靠近人了思维

在Go中,如果一个struct嵌套了另一个匿名结构体,那么这个结构体可以直接访问匿名结构体字段和方法,从而实现了继承特性

  • 基本语法
1
2
3
4
5
6
7
8
type S1 struct {
...
}

type S2 struct {
S1 // 这里嵌套匿名结构体S1
...
}

继承的要点

  1. 结构体可以使用嵌套匿名结构体所有的字段和方法,无论首字母大小写
  2. 当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望匿名结构体的字段和方法,可以通过匿名结构体名来区别
  3. 结构体嵌入两个或多个匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指明匿名结构体名字,否则编译报错
  4. 嵌套匿名结构体后,也可以在创建结构体变量时,直接指定各个匿名结构体自动的值
  5. 基本数据类型也是可以当匿名类使用的
  6. 为了保证代码的简洁性,建议不要使用多继承
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package main 

import (
"fmt"
)

// 基类1定义
type Student struct {
num int
name string
Age int
Sex string
}

func (s *Student) Init(n int, na string, a int) {
s.num = n
s.name = na
s.Age = a
}

func (s *Student) AskQuestion() {
fmt.Printf("学生 %s 回答问题\n", s.name)
}

// 基类2定义
type Child struct {
Sex string
}

func (s *Child) AskQuestion() {
fmt.Println("孩子回答问题")
}

// 定义子类1
type Pupil struct {
Student
Child
}

// 重写方法
func (s *Pupil) AskQuestion() {
fmt.Printf("小学生 %s 回答问题\n", s.name)
}

// 定义子类2
type Graduate struct {
Student
}

// 重写方法
func (s *Graduate) AskQuestion() {
fmt.Printf("大学生 %s 回答问题\n", s.name)
}

// 定义子类3
type Middle struct {
Student
}

// 基本类型使用
type Base struct {
int
}

func main() {
var s1 = Pupil{ Student {1, "111", 11, "男"}, Child {"男"} }

// 访问匿名类数据
fmt.Println(s1.Age, s1.Student.Age)
fmt.Println(s1.Child.Sex, s1.Student.Sex)

var s2 = Graduate{}
s2.Init(2, "222", 22)

var s3 = Middle{}
s3.Init(3, "333", 33)

s1.Child.AskQuestion()
s1.Student.AskQuestion()
s2.AskQuestion()
s3.AskQuestion()

// 基本类型
var b1 = Base {}
b1.int = 1
fmt.Println(b1)
}

接口

interface类型可以定义一组方法,但是这些不需要事先。并且interface不能包含任何变量

  • 基本语法
1
2
3
4
type 接口名 interface {
方法1(参数列表) 返回值列表
...
}

接口要点

  1. 接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态和高内聚低耦合的思想
  2. Go中的接口,不需要显示的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现了这个接口。
  3. 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量
  4. 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
  5. 一个自定义类型可以实现多个接口
  6. 一个接口可以继承多个别的接口
  7. interface类型默认是一个指针(引用类型)
  8. 空接口interface{}没有任何方法,所以所有类型都实现了空接口

接口和继承

  1. 继承主要在于解决代码的复用性和可维护性
  2. 接口主要在于设计好各种规范,让其他自定义类型去实现这些方法,解决代码解耦
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package main 

import (
"fmt"
)

// 接口继承
type Animation interface {
Run()
}

type Human interface {
Move()
}

type Man interface {
Human
Animation
Say()
}


type Student interface{
AskQuestion()
}

type Pupil struct {

}

func (s Pupil) AskQuestion() {
fmt.Println("小学生回答问题")
}

type Graduate struct {
}

func (s Graduate) AskQuestion() {
fmt.Println("大学生回答问题")
}

type Class struct {

}

func (c Class)Question(s Student) {
s.AskQuestion()
}

func main() {
var p = Pupil {}
var g = Graduate {}
var c = Class {}

c.Question(p)
c.Question(g)
}

多态

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

多态特征

  1. 多态参数
  2. 多态数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package main 

import (
"fmt"
)

type Student interface{
AskQuestion()
}

type Pupil struct {

}

func (s Pupil) AskQuestion() {
fmt.Println("小学生回答问题")
}

type Graduate struct {
}

func (s Graduate) AskQuestion() {
fmt.Println("大学生回答问题")
}

type Class struct {

}

func (c Class)Question(s Student) {
s.AskQuestion()
}

func main() {
var p = Pupil {}
var g = Graduate {}
var c = Class {}

// 多态参数
c.Question(p)
c.Question(g)

// 多态数组
var arrStu = [3]Student {
Pupil {},
Graduate {},
Pupil {},
}

fmt.Println(arrStu)
}