Go笔记(3)面向对象编程

这篇记录面向对象编程的写法,类、接口、多态、不支持继承。

基础知识

  1. 接口定义对象之间的交互协议,go 接口为非入侵性,实现不依赖于接口定义;接口的定义可以包含在接口使用者包内。
  2. 面向对象的扩展一般通过复合或继承来实现,go 本身不支持继承。
  3. 空接⼝可以表示任何类型;通过断⾔来将空接⼝转换为制定类型。i, ok := p.(int) // ok=true时转换成功
  4. Go 接⼝最佳实践:倾向于使⽤⼩的接⼝定义,很多接⼝只包含⼀个⽅法;较⼤的接⼝定义,可以由多个⼩接⼝定义组合⽽成;只依赖于必要功能的最⼩接⼝。

简单语法示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Employee struct { // struct定义一个实例
Id string
Name string
Age int
}
func TestCreateEmployeeObj(t *testing.T) {
e := Employee{"0", "Bob", 20}
e1 := Employee{Name: "Mike", Age: 30}
e2 := new(Employee) // 返回指针
e2.Name = "Rose"
t.Logf("e is %T", e) // %T表示返回类型,encap.Employee,&e-》*encap.Employee
t.Logf("e2 is %T", e2) // *encap.Employee
}
// 若写为 func (e Employee) 在实例对应方法被调用时,实例的成员会进行值复制
// 为了避免内存拷贝一般采用以下方式
func (e *Employee) String() string {
fmt.Printf("Address is %x", unsafe.Pointer(&e.Name))
return fmt.Sprintf("ID:%s/Name:%s/Age:%d", e.Id, e.Name, e.Age) // 把方法绑定在实例上后,可以使用实例来访问封装的数据
}

接口 & 多态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type Code string // 类型定义别名
type Programmer interface { // 类型接口
WriteHelloWorld() Code
}
// struct代表实例,实现了Programmer
// 没有使用implements这种对实现的接口进行绑定
type GoProgrammer struct {}
func (g *GoProgrammer) WriteHelloWorld() Code {
return "fmt.Println(\"Hello World\")"
}
type JavaProgrammer struct {}
func (p *JavaProgrammer) WriteHelloWorld() Code {
return "System.out.Println(\"Hello World!\")"
}
func writeFirstProgrammer(p Programmer) {
fmt.Printf("%T %v\n", p, p.WriteHelloWorld) // %T输出实例的类型
}
func TestClient(t *testing.T) {
goProg := &GoProgrammer{} // 对应指针类型的实例
javaProg := new(JavaProgrammer)
writeFirstProgram(goProg)
writeFirstProgram(javaProg)
}

继承的实现(本身不支持)

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
type Pet struct {}
func (p *Pet) Speak() {
fmt.Print("...")
}
func (p *Pet) SpeakTo(host string) {
p.Speak()
fmt.Println(" ", host)
}
/**
扩展功能,可调用内部方法
*/
type Dog struct { p *Pet } // 扩展功能
func (d *Dog) Speak() {
d.p.Speak() // 调用内部方法
}
/**
匿名嵌套类型,不能当成继承来用,不支持访问子类的方法、数据,即不支持重载
不支持LSP(子类交换原则,所有父类支持的场合可以把子类放进去)
*/
type Dog struct { Pet }
func (d *Dog) Speak() { // 方法重载
fmt.Print("Wang!")
}
func TestDog(t *testing.T) {
dog := new(Dog) // var dog Pet = new(Dog) 错误,go不支持显式类型转换
dog.SpeakTo("Chao") // 调用的Speak是父类的,不是子类重载的
}

空接口

空接口可以表示任意类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
func DoSomething(p interface{}) {
// if i, ok := p.(int); ok { // 传入的参数被断言为int类型
// fmt.Println("Integer", i)
// return
// }
switch v := p.(type) {
case int:
fmt.Println("Integer", v)
case string:
fmt.Println("String", v)
default:
fmt.Println("Unknow Type")
}
}

func TestEmptyInterfaceAssertion(t *testing.T) {
DoSomething(10)
DoSomething("10")
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!