Golang——组合和代码复用

4,567 阅读2分钟

golang中没有继承的概念,代码复用是通过组合的方式实现。本文简单介绍golang中组合的使用方法和一些细节。直接先上代码:

package main

import "fmt"

type Person struct {
	Name string
}

func (p *Person) PrintName() {
	fmt.Println("I am a person, ", p.Name)
}

type Teacher struct {
	Person
	Title string
}

func (t *Teacher) PrintInfo() {
	info := fmt.Sprintf("I am a teacher, my name is %s, my title is %s", t.Name, t.Title)
	fmt.Println(info)
}

func main() {
	t := &Teacher{Title: "Director", Person: Person{"Bob"}}
	t.PrintInfo()

	t.PrintName()
}

Teacher中组合了Person,首先需要注意的是Teacher的初始化方式:

t := &Teacher{Title: "Director", Person: Person{"Bob"}}

其次,Teacher对象可以调用Person的方法和成员变量:

t.Name
t.PrintName()

上述例子输出:

I am a teacher, my name is Bob, my title is Director
I am a person,  Bob

如果Teacher中包含的是一个Person对象,而不是组合Person,那又会怎样呢?

package main

import "fmt"

type Person struct {
	Name string
}

func (p *Person) PrintName() {
	fmt.Println("I am a person, ", p.Name)
}

type Teacher struct {
        //此处包含一个Person对象
	p     Person
	Title string
}

func (t *Teacher) PrintInfo() {
	info := fmt.Sprintf("I am a teacher, my name is %s, my title is %s", t.p.Name, t.Title)
	fmt.Println(info)
}

func main() {
	t := &Teacher{Title: "Director", p: Person{"Bob"}}
	t.PrintInfo()

	t.p.PrintName()
}

此时p作为Teacher的成员变量,Teacher的初始化方式有很大不同,同时调用Person函数的方法也需要p对象来调用。
那么如果将Person实现为interface,Teacher实现该interface, 也能实现代码模块化和某种规则约定, 如下代码:

package main

import "fmt"

type Person interface {
	PrintName()
}

type Teacher struct {
	Title string
	Name  string
}

func PrintInfo(t Person) {
	info := fmt.Sprintf("I am a teacher, my name is %s, my title is %s", t.(Teacher).Name, t.(Teacher).Title)
	fmt.Println(info)
}

func (t Teacher) PrintName() {
	fmt.Println("My name is ", t.Name)
}

func main() {
	t := Teacher{Title: "Director", Name: "Bob"}
	PrintInfo(t)
}

输出:

I am a teacher, my name is Bob, my title is Director

这里PrintName为接口约定的函数,凡是实现了该函数的struct就认为实现了该接口。如果将PrintName的接收者参数改为指针形式呢?

package main

import "fmt"

type Person interface {
	PrintName()
}

type Teacher struct {
	Title string
	Name  string
}

func PrintInfo(t Person) {
	info := fmt.Sprintf("I am a teacher, my name is %s, my title is %s", t.(*Teacher).Name, t.(*Teacher).Title)
	fmt.Println(info)
}

func (t *Teacher) PrintName() {
	fmt.Println("My name is ", t.Name)
}

func main() {
	t := Teacher{Title: "Director", Name: "Bob"}
	PrintInfo(&t)
}

改为指针接收者以后,即认为指针类型实现了该接口,因此传递给PrintInfo的参数也必须是指针类型,因为非指针类型并没有实现接口,类型不匹配。会报如下的错误:

cannot use t (variable of type Teacher) as Person value in argument to  
PrintInfo: missing method PrintName (PrintName has pointer receiver)