go面向对象
- Go也支持面向对象编程,但是和传统的面向对象编程有区别,并不是纯粹的面向对象语言。所以我们说go支持面向对象编程。
- go没有类,go语言的结构体string和其他编程语言的类有相同的地位
- go面向对象编程非常简单,去掉了传统OOP语言的继承、方法重载、构造函数和析构函数、隐藏的this指针等。
- go仍然有面向对象编程的继承,封装和多态的特效,只是实现的方式和其他OOP语言不一样
- go面向对象很优雅,OOP本身就是语言类型系统的一部分,通过接口interface关联,耦合性低,也非常灵活
结构体
- 结构体是自定义数据类型,代表一类事物
- 结构体变量时具体的,实际的,代表一个具体变量
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() { var cat1 Cat; cat1.Name = "c1"
fmt.Println(cat1)
var cat2 Cat = Cat { "Name", 20, "Red"}
fmt.Println(cat2)
var cat3 *Cat = new(Cat) (*cat3).Name = "c3"
cat3.Age = 11
fmt.Println(*cat3)
var cat4 *Cat = &Cat{} (*cat4).Name = "c4"
fmt.Println(*cat4) }
|
结构体要点
- 结构体是用户单独定义的类型,和其他类型进行转换时需要完成相同的字段(名字、个数和类型)
- 结构体进行type重新定义(相当于取别名),Go任务是新的数据类型,但是相互之间可以强转
- 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)
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 返回值列表 }
|
结构体要点
- 如果希望在方法中,修改结构体变量的值,可以通过结构体指针的方式处理
- go中的方法作用字指定的类型上,因此自定义类型都可以有方法,不仅仅是struct,int等也可以
- 方法的访问规则跟函数一样,首字母大小表示公有
- 如果一个类型实现了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" }
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)
a.test2() (&a).test2()
var i interger = 10 i.test3() i.test4() }
|
封装
封装就是把抽象出的字段和对字段操作封装在一起,数据被包含在内部,程序的其他包只有通过被授权(方法),才能对字段进行操作.
在go开发中没有特别强调封装,这点不想其他语言。
封装的好处
- 隐藏实现细节
- 可以对数据进行验证,保证安全合理
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 ... }
|
继承的要点
- 结构体可以使用嵌套匿名结构体所有的字段和方法,无论首字母大小写
- 当结构体和匿名结构体有相同的字段或者方法时,编译器采用就近访问原则访问,如希望匿名结构体的字段和方法,可以通过匿名结构体名来区别
- 结构体嵌入两个或多个匿名结构体,如两个匿名结构体有相同的字段和方法(同时结构体本身没有同名的字段和方法),在访问时,就必须明确指明匿名结构体名字,否则编译报错
- 嵌套匿名结构体后,也可以在创建结构体变量时,直接指定各个匿名结构体自动的值
- 基本数据类型也是可以当匿名类使用的
- 为了保证代码的简洁性,建议不要使用多继承
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" )
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) }
type Child struct { Sex string }
func (s *Child) AskQuestion() { fmt.Println("孩子回答问题") }
type Pupil struct { Student Child }
func (s *Pupil) AskQuestion() { fmt.Printf("小学生 %s 回答问题\n", s.name) }
type Graduate struct { Student }
func (s *Graduate) AskQuestion() { fmt.Printf("大学生 %s 回答问题\n", s.name) }
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(参数列表) 返回值列表 ... }
|
接口要点
- 接口里的所有方法都没有方法体,即接口的方法都是没有实现的方法。接口体现了程序设计的多态和高内聚低耦合的思想
- Go中的接口,不需要显示的实现。只要一个变量,含有接口类型中的所有方法,那么这个变量就实现了这个接口。
- 接口本身不能创建实例,但是可以指向一个实现了该接口的自定义类型的变量
- 只要是自定义数据类型,就可以实现接口,不仅仅是结构体类型
- 一个自定义类型可以实现多个接口
- 一个接口可以继承多个别的接口
- interface类型默认是一个指针(引用类型)
- 空接口interface{}没有任何方法,所以所有类型都实现了空接口
接口和继承
- 继承主要在于解决代码的复用性和可维护性
- 接口主要在于设计好各种规范,让其他自定义类型去实现这些方法,解决代码解耦
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 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) }
|