前言
Go语言并不像Java那样有类的概念,以及extends这样的关键字,但是可以用其特有的数据结构来实现类似面向对象的特性。主要有结构体实现封装,组合实现继承,接口实现多态。
封装可以隐藏类的实现细节并使代码具备模块化,继承可以扩展已存在的模块,多态的作用除了复用性外,还可以解决模块之间高耦合的问题。
一、结构体实现封装
在Go语言中,我们可以对结构体的字段进行封装,并通过结构体中的方法来操作内部的字段。如果结构体中字段名的首字母是小写字母,那么这样的字段是私有的,相当于private字段。外部包裹能直接访问,如果是在名的首字母是大写字母,那么这样的字段对外暴露的,相当于public字段。能够起的方法也是一样的,如果方法名首字母是大写字母,那么这样的方法对外暴露的。
下面案例体现了封装特性:
package main
type Person struct {
name string
age int
}
func (p *Person) SetName(name string) {
p.name = name
}
func (p *Person) SetAge(age int) {
p.age = age
}
func (p *Person) GetName() string{
return p.name
}
func (p *Person) GetAge() int {
return p.age
}
二、组合实现继承
面向对象的继承特性实际上就是一种组件复用的机制。在Java中可以先定一个父类,然后通过继承特性来实现子类继承父类的功能。但Go语言中没有继承的关键字extends,而是采用组合的方式来实现继承的效果,组合和继承是有区别的,组合可以理解为has - a 关系,继承可以理解为is - a的关系。但他们都实现了代码复用的目的。组合相对于继承的优点有:
- 可以利用面向接口编程原则的一系列优点,封装性行耦合性低
- 相对于继承的编译期确定实现,组合的运行态指定实现更加灵活
- 组合是非侵入式的,继承是侵入式的
父类Person类:
type Person struct {
name string
age int
}
func (p *Person) Eat() {
fmt.println("Person Eat")
}
func (p *Person) Walk() {
fmt.Println("Person Walk")
}
子类Student继承Person类:
type Student struct {
Person //组合Person,注意首字母大写,否则无法继承属性和方法
school string
}
func (s *Student) study() {
fmt.Println(s.name, "study") //调用了name,这里的name就是继承自person结构体的
}
//重写方法,会覆盖Person中的walk方法
func (s *Student) Walk() {
fmt.Println(s.name, "walk")
}
三、接口实现多态
接口定义与实现
接口是一种对约定标准进行定义的,如果一个结构体嵌入了接口类型,那么任何其他类型实现了该接口都可以与之进行组合调用。接口实现最明显的优点就是实现了类和接口的分离,在切换实现类的时候不用更换接口功能。
在Go语言中定义接口的语法如下:
type 接口名 interface {
方法
}
在Go语言中对接口的实现只需要某个类型T实现了接口中的方法,就相当于实现了该接口。定义接口并实现的示例如下:
import "fmt"
//定义一个考试接口
type exam interface {
exam()
}
type Student struct {
Person //组合Person,注意首字母大写,否则无法继承属性和方法
school string
}
//实现了exam接口
func (s *Student) exam() {
fmt.Println(s.name, "exam")
}
接口实现多态
在面向对象的语言中,接口的多种不同实现方式即为多态。多态的示例如下:
package main
import "fmt"
//Person接口
type Person interface {
ToSchool()
}
//学生类
type Student struct {
work string
}
//学生类实现Person接口
func (this *Student) ToSchool() {
fmt.Println("Student ", this.work)
}
//老师类
type Teacher struct {
work string
}
//老师类实现Person接口
func (this *Teacher) ToSchool() {
fmt.Println("Teacher ", this.work)
}
//工厂模式函数,根据传入工作不同动态返回不同类型
func Factory(work string) Person {
switch work {
case "study":
return &Student{work: "study"}
case "teach":
return &Teacher{work: "teach"}
default:
panic("no such profession")
}
}
func main() {
person := Factory("study")
person.ToSchool() //Student study
person = Factory("teach")
person.ToSchool() //Teacher teach
}